make/scripts/webrev.ksh

Tue, 26 Mar 2013 13:41:36 -0400

author
jgish
date
Tue, 26 Mar 2013 13:41:36 -0400
changeset 678
26a4456cb19e
parent 643
c641268c4532
child 690
b95c5c8ee60a
permissions
-rw-r--r--

8009824: webrev.ksh generated jdk.patch files do not handle renames, copies, and shouldn't be applied
Summary: use hg export --git to produce proper patch file
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=23.18-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         NO_OUTGOING=
  1447         print " File list from hg fstatus -mard ...\c"
  1448         FSTAT_OPT=
  1449         fstatus
  1450         HG_LIST_FROM_COMMIT=0
  1451     fi
  1452     print " Done."
  1456 # Used when dealing with the result of 'hg foutgoing'
  1457 # When now go down the tree and generate the change list
  1459 function treestatus
  1461     TREE=$1
  1462     HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
  1464     $HGCMD -mdn 2>/dev/null | $FILTER | while read F
  1465     do
  1466         echo $TREE/$F
  1467     done >> $FLIST
  1469     # Then all the added files
  1470     # But some of these could have been "moved" or renamed ones or copied ones
  1471     # so let's make sure we get the proper info
  1472     # hg status -aC will produce something like:
  1473     #	A subdir/File3
  1474     #	A subdir/File4
  1475     #	  File4
  1476     #	A subdir/File5
  1477     # The first and last are simple addition while the middle one
  1478     # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
  1479     # without also getting the status of removed files.  The middle case above
  1480     # is a rename if File4 is also shown a being removed.  If File4 is not a 
  1481     # removed file, then the middle case is a copy from File4 to subdir/File4
  1482     # FIXME - we're not distinguishing copy from rename
  1483     $HGCMD -aC | $FILTER | while read LINE; do
  1484 	ldone=""
  1485 	while [ -z "$ldone" ]; do
  1486 	    ldone="1"
  1487 	    set - $LINE
  1488 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1489 		AFILE=$2
  1490 		if read LINE2; then
  1491 		    set - $LINE2
  1492 		    if [ $# -eq 1 ]; then
  1493 			echo $TREE/$AFILE $TREE/$1 >>$FLIST
  1494 		    elif [ $# -eq 2 ]; then
  1495 			echo $TREE/$AFILE >>$FLIST
  1496 			LINE=$LINE2
  1497 			ldone=""
  1498 		    fi
  1499 		else
  1500 		    echo $TREE/$AFILE >>$FLIST
  1501 		fi
  1502 	    fi
  1503 	done
  1504     done
  1505     $HGCMD -rn | $FILTER | while read RFILE; do
  1506 	grep "$TREE/$RFILE" $FLIST >/dev/null
  1507 	if [ $? -eq 1 ]; then
  1508 	    echo $TREE/$RFILE >>$FLIST
  1509 	fi
  1510     done
  1513 function fstatus
  1516     # forest extension is still being changed. For instance the output
  1517     # of fstatus used to no prepend the tree path to filenames, but
  1518     # this has changed recently. AWK code below does try to handle both
  1519     # cases
  1521     hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1522 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1523 	$1 != ""	{n=index($1,tree);
  1524 			 if (n == 0)
  1525 				{ printf("%s/%s\n",tree,$1)}
  1526 			 else
  1527 				{ printf("%s\n",$1)}}' >> $FLIST
  1530     # There is a bug in the output of fstatus -aC on recent versions: it
  1531     # inserts a space between the name of the tree and the filename of the
  1532     # old file. e.g.:
  1534     # $ hg fstatus -aC
  1535     # [.]
  1537     # [MyWS]
  1538     # A MyWS/subdir/File2
  1539     #  MyWS/ File2
  1541     # [MyWS2]
  1544     hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1545 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1546 	/^A .*/		{n=index($2,tree);
  1547 			 if (n == 0)
  1548 				{ printf("A %s/%s\n",tree,$2)}
  1549 			 else
  1550 				{ printf("A %s\n",$2)};
  1551 			 next}
  1552 	/^ /		{n=index($1,tree);
  1553 			 if (n == 0)
  1554 				{ printf("%s/%s\n",tree,$1)}
  1555 			 else
  1556 				{ if (NF == 2)
  1557 					printf("%s/%s\n",tree,$2)
  1558 				  else
  1559 					printf("%s\n",$1)
  1560 				};
  1561 			 next}
  1562 	' | while read LINE; do
  1563 	ldone=""
  1564 	while [ -z "$ldone" ]; do
  1565 	    ldone="1"
  1566 	    set - $LINE
  1567 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1568 		AFILE=$2
  1569 		if read LINE2; then
  1570 		    set - $LINE2
  1571 		    if [ $# -eq 1 ]; then
  1572 			echo $AFILE $1 >>$FLIST
  1573 		    elif [ $# -eq 2 ]; then
  1574 			echo $AFILE >>$FLIST
  1575 			LINE=$LINE2
  1576 			ldone=""
  1577 		    fi
  1578 		else
  1579 		    echo $AFILE >>$FLIST
  1580 		fi
  1581 	    fi
  1582 	done
  1583     done
  1584     hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1585 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1586 	$1 != ""	{n=index($1,tree);
  1587 			 if (n == 0)
  1588 				{ printf("%s/%s\n",tree,$1)}
  1589 			 else
  1590 				{ printf("%s\n",$1)}}' | while read RFILE; do
  1591 	grep "$RFILE" $FLIST >/dev/null
  1592 	if [ $? -eq 1 ]; then
  1593 	    echo $RFILE >>$FLIST
  1594 	fi
  1595     done
  1599 # flist_from_mercurial $PWS
  1601 # Only local file based repositories are supported at present
  1602 # since even though we can determine the list from the parent finding
  1603 # the changes is harder.
  1605 # We first look for any outgoing files, this is for when the user has
  1606 # run hg commit.  If we don't find any then we look with hg status.
  1608 # We need at least one of default-push or default paths set in .hg/hgrc
  1609 # If neither are set we don't know who to compare with.
  1611 function flist_from_mercurial
  1613 #	if [ "${PWS##ssh://}" != "$PWS" -o \
  1614 #	     "${PWS##http://}" != "$PWS" -o \
  1615 #	     "${PWS##https://}" != "$PWS" ]; then
  1616 #		print "Remote Mercurial repositories not currently supported."
  1617 #		print "Set default and/or default-push to a local repository"
  1618 #		exit
  1619 #	fi
  1620     if [[ -n $forestflag ]]; then
  1621         HG_LIST_FROM_COMMIT=
  1622 	flist_from_mercurial_forest
  1623     else
  1624         STATUS_REV=
  1625         if [[ -n $rflag ]]; then
  1626             STATUS_REV="--rev $PARENT_REV"
  1627         elif [[ -n $OUTREV ]]; then
  1628             STATUS_REV="--rev $OUTREV"
  1629         else
  1630             # hg commit hasn't been run see what is lying around
  1631             print "\n No outgoing, perhaps you haven't commited."
  1632             NO_OUTGOING=
  1633         fi
  1634 	# First let's list all the modified or deleted files
  1636 	hg status $STATUS_REV -mdn | $FILTER > $FLIST
  1638 	# Then all the added files
  1639 	# But some of these could have been "moved" or renamed ones
  1640 	# so let's make sure we get the proper info
  1641 	# hg status -aC will produce something like:
  1642 	#	A subdir/File3
  1643 	#	A subdir/File4
  1644 	#	  File4
  1645 	#	A subdir/File5
  1646         # The first and last are simple addition while the middle one
  1647         # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
  1648         # without also getting the status of removed files.  The middle case above
  1649         # is a rename if File4 is also shown a being removed.  If File4 is not a 
  1650         # removed file, then the middle case is a copy from File4 to subdir/File4
  1651         # FIXME - we're not distinguishing copy from rename
  1653 	hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
  1654 	while read LINE; do
  1655 	    ldone=""
  1656 	    while [ -z "$ldone" ]; do
  1657 		ldone="1"
  1658 		set - $LINE
  1659 		if [ $# -eq 2 -a "$1" == "A" ]; then
  1660 		    AFILE=$2
  1661 		    if read LINE2; then
  1662 			set - $LINE2
  1663 			if [ $# -eq 1 ]; then
  1664 			    echo $AFILE $1 >>$FLIST
  1665 			elif [ $# -eq 2 ]; then
  1666 			    echo $AFILE >>$FLIST
  1667 			    LINE=$LINE2
  1668 			    ldone=""
  1669 			fi
  1670 		    else
  1671 			echo $AFILE >>$FLIST
  1672 		    fi
  1673 		fi
  1674 	    done
  1675 	done < $FLIST.temp
  1676 	hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
  1677 	while read RFILE; do
  1678 	    grep "$RFILE" $FLIST >/dev/null
  1679 	    if [ $? -eq 1 ]; then
  1680 		echo $RFILE >>$FLIST
  1681 	    fi
  1682 	done < $FLIST.temp
  1683 	rm -f $FLIST.temp
  1684     fi
  1687 function env_from_flist
  1689 	[[ -r $FLIST ]] || return
  1692 	# Use "eval" to set env variables that are listed in the file
  1693 	# list.  Then copy those into our local versions of those
  1694 	# variables if they have not been set already.
  1696 	eval `sed -e "s/#.*$//" $FLIST | grep = `
  1698 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  1701 	# Check to see if CODEMGR_PARENT is set in the flist file.
  1703 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  1704 	    codemgr_parent=$CODEMGR_PARENT
  1708 # detect_scm
  1710 # We dynamically test the SCM type; this allows future extensions to
  1711 # new SCM types
  1713 function detect_scm
  1716 	# If CODEMGR_WS is specified in the flist file, we assume teamware.
  1718 	if [[ -r $FLIST ]]; then
  1719 		egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
  1720 		if [[ $? -eq 0 ]]; then
  1721 			print "teamware"
  1722 			return
  1723 		fi
  1724 	fi
  1727 	# The presence of $CODEMGR_WS and a Codemgr_wsdata directory
  1728 	# is our clue that this is a teamware workspace.
  1729 	# Same if true if current directory has a Codemgr_wsdata sub-dir
  1731 	if [[ -z "$CODEMGR_WS" ]]; then
  1732 	    CODEMGR_WS=`workspace name 2>/dev/null`
  1733 	fi
  1735 	if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
  1736 		print "teamware"
  1737 	elif [[ -d $PWD/Codemgr_wsdata ]]; then
  1738 		print "teamware"
  1739 	elif hg root >/dev/null ; then
  1740 		print "mercurial"
  1741 	else
  1742 		print "unknown"
  1743 	fi
  1747 # Extract the parent workspace from the Codemgr_wsdata/parent file
  1749 function parent_from_teamware
  1751     if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
  1752 	tail -1 "$1/Codemgr_wsdata/parent"
  1753     fi
  1756 function look_for_prog
  1758 	typeset path
  1759 	typeset ppath
  1760 	typeset progname=$1
  1762 	DEVTOOLS=
  1763 	OS=`uname`
  1764 	if [[ "$OS" == "SunOS" ]]; then
  1765 	    DEVTOOLS="/java/devtools/`uname -p`/bin"
  1766 	elif [[ "$OS" == "Linux" ]]; then
  1767 	    DEVTOOLS="/java/devtools/linux/bin"
  1768 	fi
  1770 	ppath=$PATH
  1771 	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
  1772 	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
  1773 	ppath=$ppath:/opt/onbld/bin/`uname -p`
  1774 	ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
  1776 	PATH=$ppath prog=`whence $progname`
  1777 	if [[ -n $prog ]]; then
  1778 		print $prog
  1779 	fi
  1782 function build_old_new_teamware
  1784 	# If the child's version doesn't exist then
  1785 	# get a readonly copy.
  1787 	if [[ ! -f $F && -f SCCS/s.$F ]]; then
  1788 		$SCCS get -s $F
  1789 	fi
  1792 	# Snag new version of file.
  1794 	rm -f $newdir/$DIR/$F
  1795 	cp $F $newdir/$DIR/$F
  1798 	# Get the parent's version of the file. First see whether the
  1799 	# child's version is checked out and get the parent's version
  1800 	# with keywords expanded or unexpanded as appropriate.
  1802 	if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
  1803 	    -f $PWS/$PDIR/SCCS/p.$PF ]; then
  1804 		rm -f $olddir/$PDIR/$PF
  1805 		if [ -f SCCS/p.$F ]; then
  1806 			$SCCS get -s -p -k $PWS/$PDIR/$PF \
  1807 			    > $olddir/$PDIR/$PF
  1808 		else
  1809 			$SCCS get -s -p    $PWS/$PDIR/$PF \
  1810 			    > $olddir/$PDIR/$PF
  1811 		fi
  1812 	else
  1813 		if [[ -f $PWS/$PDIR/$PF ]]; then
  1814 			# Parent is not a real workspace, but just a raw
  1815 			# directory tree - use the file that's there as
  1816 			# the old file.
  1818 			rm -f $olddir/$DIR/$F
  1819 			cp $PWS/$PDIR/$PF $olddir/$DIR/$F
  1820 		fi
  1821 	fi
  1825 # Find the parent for $1
  1827 function find_outrev
  1829     crev=$1
  1830     prev=`hg log -r $crev --template '{parents}\n'`
  1831     if [[ -z "$prev" ]]
  1832     then
  1833 	# No specific parent means previous changeset is parent
  1834 	prev=`expr $crev - 1`
  1835     else
  1836 	# Format is either of the following two:
  1837 	# 546:7df6fcf1183b
  1838 	# 548:16f1915bb5cd 547:ffaa4e775815
  1839 	prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
  1840     fi
  1841     print $prev
  1844 function extract_ssh_infos
  1846     CMD=$1
  1847     if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
  1848 	ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
  1849 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
  1850 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
  1851     else
  1852 	ssh_user=
  1853 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
  1854 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
  1855     fi
  1859 function build_old_new_mercurial
  1861 	olddir=$1
  1862 	newdir=$2
  1863 	DIR=$3
  1864 	F=$4
  1866 	# new version of the file.
  1868 	rm -rf $newdir/$DIR/$F
  1869 	if [ -f $F ]; then
  1870 	    cp $F  $newdir/$DIR/$F
  1871 	fi
  1874 	# Old version of the file.
  1876 	rm -rf $olddir/$DIR/$F
  1878 	if [ -n "$PWS" ]; then
  1879 	    if expr "$PWS" : 'ssh://' >/dev/null
  1880 	    then
  1881 		extract_ssh_infos $PWS
  1882 		if [ -n "$ssh_user" ]; then
  1883 		    parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1884 		else
  1885 		    parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1886 		fi
  1887 	    else
  1888 		parent="hg -R $PWS --cwd $PWS"
  1889 	    fi
  1890 	else
  1891 	    parent=""
  1892 	fi
  1894 	if [ -z "$rename" ]; then
  1895 	    if [ -n "$rflag" ]; then
  1896 		parentrev=$PARENT_REV
  1897 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1898                 parentrev=$OUTREV
  1899 	    else
  1900                 if [[ -n $HG_BRANCH ]]; then
  1901                     parentrev=$HG_BRANCH
  1902                 else
  1903 		    parentrev="tip"
  1904                 fi
  1905 	    fi
  1907 	    if [ -n "$parentrev" ]; then
  1908 		if [ -z "$parent" ]; then
  1909 		    hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
  1910 		else
  1911 		    # when specifying a workspace we have to provide
  1912 		    # the full path
  1913 		    $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
  1914 		fi
  1915 	    fi
  1916 	else
  1917 	    # It's a rename (or a move), or a copy, so let's make sure we move
  1918 	    # to the right directory first, then restore it once done
  1919 	    current_dir=`pwd`
  1920 	    cd $CWS/$PDIR
  1921 	    if [ -n "$rflag" ]; then
  1922 		parentrev=$PARENT_REV
  1923 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1924                 parentrev=$OUTREV
  1925 	    fi
  1926 	    if [ -z "$parentrev" ]; then
  1927 		parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
  1928 	    fi
  1929 	    if [ -n "$parentrev" ]; then
  1930 		mkdir -p $olddir/$PDIR
  1931 		if [ -z "$parent" ]; then
  1932 		    hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
  1933 		else
  1934 		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1935 		fi
  1936 	    fi
  1937 	    cd $current_dir
  1938 	fi
  1941 function build_old_new
  1943 	if [[ $SCM_MODE == "teamware" ]]; then
  1944 		build_old_new_teamware $@
  1945 	fi
  1947 	if [[ $SCM_MODE == "mercurial" ]]; then
  1948 		build_old_new_mercurial $@
  1949 	fi
  1954 # Usage message.
  1956 function usage
  1958 	print "Usage:\twebrev [common-options]
  1959 	webrev [common-options] ( <file> | - )
  1960 	webrev [common-options] -w <wx file>
  1961 	webrev [common-options] -l [arguments to 'putback']
  1963 Options:
  1964 	-v: Print the version of this tool.
  1965         -b: Do not ignore changes in the amount of white space.
  1966         -c <CR#>: Include link to CR (aka bugid) in the main page.
  1967 	-O: Print bugids/arc cases suitable for OpenJDK.
  1968 	-i <filename>: Include <filename> in the index.html file.
  1969 	-o <outdir>: Output webrev to specified directory.
  1970 	-p <compare-against>: Use specified parent wkspc or basis for comparison
  1971 	-w <wxfile>: Use specified wx active file.
  1972         -u <username>: Use that username instead of 'guessing' one.
  1973 	-m: Forces the use of Mercurial
  1974 	-t: Forces the use of Teamware
  1976 Mercurial only options:
  1977 	-r rev: Compare against a specified revision
  1978 	-N: Skip 'hg outgoing', use only 'hg status'
  1979 	-f: Use the forest extension
  1981 Environment:
  1982 	WDIR: Control the output directory.
  1983 	WEBREV_BUGURL: Control the URL prefix for bugids.
  1984 	WEBREV_SACURL: Control the URL prefix for ARC cases.
  1986 SCM Environment:
  1987 	Teamware: CODEMGR_WS: Workspace location.
  1988 	Teamware: CODEMGR_PARENT: Parent workspace location.
  1992 	exit 2
  1997 # Main program starts here
  2000 LANG="C"
  2001 LC_ALL="C"
  2002 export LANG LC_ALL
  2003 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
  2005 set +o noclobber
  2007 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
  2008 [[ -z $WX ]] && WX=`look_for_prog wx`
  2009 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
  2010 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
  2011 [[ -z $PERL ]] && PERL=`look_for_prog perl`
  2012 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
  2013 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
  2014 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
  2015 [[ -z $AWK ]] && AWK=`look_for_prog awk`
  2016 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
  2017 [[ -z $JAR ]] && JAR=`look_for_prog jar`
  2018 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
  2019 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
  2020 [[ -z $WGET ]] && WGET=`look_for_prog wget`
  2022 if uname | grep CYGWIN >/dev/null
  2023 then
  2024         ISWIN=1
  2025         # Under windows mercurial outputs '\' instead of '/'
  2026         FILTER="tr '\\\\' '/'"
  2027 else
  2028         FILTER="cat"
  2029 fi
  2031 if [[ ! -x $PERL ]]; then
  2032 	print -u2 "Error: No perl interpreter found.  Exiting."
  2033 	exit 1
  2034 fi
  2037 # These aren't fatal, but we want to note them to the user.
  2038 # We don't warn on the absence of 'wx' until later when we've
  2039 # determined that we actually need to try to invoke it.
  2041 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
  2042 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
  2043 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
  2045 # Declare global total counters.
  2046 integer TOTL TINS TDEL TMOD TUNC
  2048 flist_mode=
  2049 flist_file=
  2050 bflag=
  2051 iflag=
  2052 oflag=
  2053 pflag=
  2054 uflag=
  2055 lflag=
  2056 wflag=
  2057 Oflag=
  2058 rflag=
  2059 Nflag=
  2060 forestflag=
  2061 while getopts "c:i:o:p:r:u:lmtwONvfb" opt
  2062 do
  2063 	case $opt in
  2064         b)      bflag=1;;
  2066 	i)	iflag=1
  2067 		INCLUDE_FILE=$OPTARG;;
  2069 	o)	oflag=1
  2070 		WDIR=$OPTARG;;
  2072 	p)	pflag=1
  2073 		codemgr_parent=$OPTARG;;
  2075 	u)      uflag=1
  2076 		username=$OPTARG;;
  2078         c)      if [[ -z $CRID ]]; then
  2079                    CRID=$OPTARG
  2080                 else
  2081                    CRID="$CRID $OPTARG"
  2082                 fi;;
  2084 	m)	SCM_MODE="mercurial";;
  2086 	t)	SCM_MODE="teamware";;
  2089 	# If -l has been specified, we need to abort further options
  2090 	# processing, because subsequent arguments are going to be
  2091 	# arguments to 'putback -n'.
  2093 	l)	lflag=1
  2094 		break;;
  2096 	w)	wflag=1;;
  2098 	O)	Oflag=1;;
  2100 	N)	Nflag=1;;
  2102 	f)	forestflag=1;;
  2104 	r)	rflag=1
  2105 		PARENT_REV=$OPTARG;;
  2107 	v)	print "$0 version: $WEBREV_UPDATED";;
  2110 	?)	usage;;
  2111 	esac
  2112 done
  2114 FLIST=/tmp/$$.flist
  2116 if [[ -n $wflag && -n $lflag ]]; then
  2117 	usage
  2118 fi
  2120 if [[ -n $forestflag && -n $rflag ]]; then
  2121     print "The -r <rev> flag is incompatible with the use of forests"
  2122     exit 2
  2123 fi
  2126 # If this manually set as the parent, and it appears to be an earlier webrev,
  2127 # then note that fact and set the parent to the raw_files/new subdirectory.
  2129 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
  2130 	parent_webrev="$codemgr_parent"
  2131 	codemgr_parent="$codemgr_parent/raw_files/new"
  2132 fi
  2134 if [[ -z $wflag && -z $lflag ]]; then
  2135 	shift $(($OPTIND - 1))
  2137 	if [[ $1 == "-" ]]; then
  2138 		cat > $FLIST
  2139 		flist_mode="stdin"
  2140 		flist_done=1
  2141 		shift
  2142 	elif [[ -n $1 ]]; then
  2143 		if [[ ! -r $1 ]]; then
  2144 			print -u2 "$1: no such file or not readable"
  2145 			usage
  2146 		fi
  2147 		cat $1 > $FLIST
  2148 		flist_mode="file"
  2149 		flist_file=$1
  2150 		flist_done=1
  2151 		shift
  2152 	else
  2153 		flist_mode="auto"
  2154 	fi
  2155 fi
  2158 # Before we go on to further consider -l and -w, work out which SCM we think
  2159 # is in use.
  2161 if [[ -z $SCM_MODE ]]; then
  2162     SCM_MODE=`detect_scm $FLIST`
  2163 fi
  2164 if [[ $SCM_MODE == "unknown" ]]; then
  2165 	print -u2 "Unable to determine SCM type currently in use."
  2166 	print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
  2167 	print -u2 "              the environment or in the file list."
  2168 	print -u2 "For mercurial: webrev runs 'hg root'."
  2169 	exit 1
  2170 fi
  2172 print -u2 "   SCM detected: $SCM_MODE"
  2175 if [[ $SCM_MODE == "mercurial" ]]; then
  2177     # determine Workspace and parent workspace paths
  2179     CWS=`hg root | $FILTER`
  2180     if [[ -n $pflag && -z "$PWS" ]]; then
  2181 	OUTPWS=$codemgr_parent
  2182         # Let's try to expand it if it's an alias defined in [paths]
  2183         tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  2184         if [[ -n $tmp ]]; then
  2185             OUTPWS="$tmp"
  2186         fi
  2187         if [[ -n $rflag ]]; then
  2188 	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
  2189 	        PWS=$codemgr_parent
  2190 	    else
  2191 	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
  2192 	    fi
  2193         fi
  2194     fi
  2196     # OUTPWS is the parent repository to use when using 'hg outgoing'
  2198     if [[ -z $Nflag ]]; then
  2199         if [[ -n $forestflag ]]; then
  2201             # for forest we have to rely on properly set default and
  2202             # default-push because they can be different from the top one.
  2203             # unless of course it was explicitely speficied with -p
  2204             if [[ -z $pflag ]]; then
  2205                 OUTPWS=
  2206             fi
  2207         else
  2209             # Unfortunately mercurial is bugged and doesn't handle
  2210             # aliases correctly in 'hg path default'
  2211             # So let's do it ourselves. Sigh...
  2212             if [[ -z "$OUTPWS" ]]; then
  2213                 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  2214             fi
  2215             # Still empty, means no default-push
  2216             if [[ -z "$OUTPWS" ]]; then
  2217                 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  2218             fi
  2219             # Let's try to expand it if it's an alias defined in [paths]
  2220             tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  2221             if [[ -n $tmp ]]; then
  2222                 OUTPWS="$tmp"
  2223             fi
  2224         fi
  2225     fi
  2227     # OUTPWS may contain username:password, let's make sure we remove the
  2228     # sensitive information before we print out anything in the HTML
  2230     OUTPWS2=$OUTPWS
  2231     if [[ -n $OUTPWS ]]; then
  2232 	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
  2233 	    # Remove everything between '://' and '@'
  2234 	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
  2235 	fi
  2236     fi
  2238     if [[ -z $HG_BRANCH ]]; then
  2239         HG_BRANCH=`hg branch`
  2240         if [ "$HG_BRANCH" == "default" ]; then
  2242             # 'default' means no particular branch, so let's cancel that
  2244             HG_BRANCH=
  2245         fi
  2246     fi
  2248     if [[ -z $forestflag ]]; then
  2249         if [[ -z $Nflag ]]; then
  2251             # If no "-N", always do "hg outgoing" against parent
  2252             # repository to determine list of outgoing revisions.
  2254             ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
  2255             if [[ -n $ALL_CREV ]]; then
  2256                 FIRST_CREV=`echo "$ALL_CREV" | head -1`
  2258                 # If no "-r", choose revision to compare against by
  2259                 # finding the latest revision not in the outgoing list.
  2261                 if [[ -z $rflag ]]; then
  2262                     OUTREV=`find_outrev "$FIRST_CREV"`
  2263                     if [[ -n $OUTREV ]]; then
  2264                         HG_LIST_FROM_COMMIT=1
  2265                     fi
  2266                 fi
  2267             fi
  2268         elif [[ -n $rflag ]]; then
  2270             # If skipping "hg outgoing" but still comparing against a
  2271             # specific revision (not the tip), set revision for comment
  2272             # accumulation.
  2274             FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
  2275             FIRST_CREV=`expr $FIRST_CREV + 1`
  2276         fi
  2277     fi
  2278     #Let's check if a merge is needed, if so, issue a warning
  2279     PREV=`hg parent | grep '^tag:.*tip$'`
  2280     if [[ -z $PREV ]]; then
  2281         print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
  2282     fi
  2283 fi
  2285 if [[ -n $lflag ]]; then
  2287 	# If the -l flag is given instead of the name of a file list,
  2288 	# then generate the file list by extracting file names from a
  2289 	# putback -n.
  2291 	shift $(($OPTIND - 1))
  2292 	if [[ $SCM_MODE == "teamware" ]]; then
  2293 		flist_from_teamware "$*"
  2294 	elif [[ $SCM_MODE == "mercurial" ]]; then
  2295 		flist_from_mercurial
  2296 	fi
  2297 	flist_done=1
  2298 	shift $#
  2300 elif [[ -n $wflag ]]; then
  2302 	# If the -w is given then assume the file list is in Bonwick's "wx"
  2303 	# command format, i.e.  pathname lines alternating with SCCS comment
  2304 	# lines with blank lines as separators.  Use the SCCS comments later
  2305 	# in building the index.html file.
  2307 	shift $(($OPTIND - 1))
  2308 	wxfile=$1
  2309 	if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
  2310 		if [[ -r $CODEMGR_WS/wx/active ]]; then
  2311 			wxfile=$CODEMGR_WS/wx/active
  2312 		fi
  2313 	fi
  2315 	[[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
  2316 	    "be auto-detected (check \$CODEMGR_WS)" && exit 1
  2318 	print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
  2319 	flist_from_wx $wxfile
  2320 	flist_done=1
  2321 	if [[ -n "$*" ]]; then
  2322 		shift
  2323 	fi
  2324 elif [[ $flist_mode == "stdin" ]]; then
  2325 	print -u2 " File list from: standard input"
  2326 elif [[ $flist_mode == "file" ]]; then
  2327 	print -u2 " File list from: $flist_file"
  2328 fi
  2330 if [[ $# -gt 0 ]]; then
  2331 	print -u2 "WARNING: unused arguments: $*"
  2332 fi
  2334 if [[ $SCM_MODE == "teamware" ]]; then
  2336 	# Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
  2337 	# be set in a number of ways, in decreasing precedence:
  2339 	#      1) on the command line (only for the parent)
  2340 	#      2) in the user environment
  2341 	#      3) in the flist
  2342 	#      4) automatically based on the workspace (only for the parent)
  2346 	# Here is case (2): the user environment
  2348 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  2349 	[[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
  2351 	if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
  2352 		print -u2 "$codemgr_ws: no such workspace"
  2353 		exit 1
  2354 	fi
  2356 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  2357 	    codemgr_parent=$CODEMGR_PARENT
  2359 	if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
  2360 		print -u2 "$codemgr_parent: no such directory"
  2361 		exit 1
  2362 	fi
  2365 	# If we're in auto-detect mode and we haven't already gotten the file
  2366 	# list, then see if we can get it by probing for wx.
  2368 	if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
  2369 		if [[ ! -x $WX ]]; then
  2370 			print -u2 "WARNING: wx not found!"
  2371 		fi
  2374 		# We need to use wx list -w so that we get renamed files, etc.
  2375 		# but only if a wx active file exists-- otherwise wx will
  2376 		# hang asking us to initialize our wx information.
  2378 		if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
  2379 			print -u2 " File list from: 'wx list -w' ... \c"
  2380 			$WX list -w > $FLIST
  2381 			$WX comments > /tmp/$$.wx_comments
  2382 			wxfile=/tmp/$$.wx_comments
  2383 			print -u2 "done"
  2384 			flist_done=1
  2385 		fi
  2386 	fi
  2389 	# If by hook or by crook we've gotten a file list by now (perhaps
  2390 	# from the command line), eval it to extract environment variables from
  2391 	# it: This is step (3).
  2393 	env_from_flist
  2396 	# Continuing step (3): If we still have no file list, we'll try to get
  2397 	# it from teamware.
  2399 	if [[ -z $flist_done ]]; then
  2400 		flist_from_teamware
  2401 		env_from_flist
  2402 	fi
  2404 	if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
  2405 	    codemgr_ws=$PWD
  2406 	fi
  2408 	# Observe true directory name of CODEMGR_WS, as used later in
  2409 	# webrev title.
  2411 	if [[ -n $codemgr_ws ]]; then
  2412 	    codemgr_ws=$(cd $codemgr_ws;print $PWD)
  2413 	fi
  2415 	if [[ -n $codemgr_parent ]]; then
  2416 	    codemgr_parent=$(cd $codemgr_parent;print $PWD)
  2417 	fi
  2420 	# (4) If we still don't have a value for codemgr_parent, get it
  2421 	# from workspace.
  2423 	[[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
  2424 	[[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
  2426 	if [[ ! -d $codemgr_parent ]]; then
  2427 	    print -u2 "$CODEMGR_PARENT: no such parent workspace"
  2428 	    exit 1
  2429 	fi
  2432 	# Reset CODEMGR_WS to make sure teamware commands are happy.
  2434 	CODEMGR_WS=$codemgr_ws
  2435 	CWS=$codemgr_ws
  2436 	PWS=$codemgr_parent
  2437 elif [[ $SCM_MODE == "mercurial" ]]; then
  2438     if [[ -z $flist_done ]]; then
  2439 	flist_from_mercurial $PWS
  2440     fi
  2441 fi
  2444 # If the user didn't specify a -i option, check to see if there is a
  2445 # webrev-info file in the workspace directory.
  2447 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
  2448 	iflag=1
  2449 	INCLUDE_FILE="$CWS/webrev-info"
  2450 fi
  2452 if [[ -n $iflag ]]; then
  2453 	if [[ ! -r $INCLUDE_FILE ]]; then
  2454 		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
  2455 		    "not readable."
  2456 		exit 1
  2457 	else
  2459 		# $INCLUDE_FILE may be a relative path, and the script alters
  2460 		# PWD, so we just stash a copy in /tmp.
  2462 		cp $INCLUDE_FILE /tmp/$$.include
  2463 	fi
  2464 fi
  2467 # Output directory.
  2469 if [[ -z $WDIR ]]; then
  2470     WDIR=$CWS/webrev
  2471 else
  2472     # If the output directory doesn't end with '/webrev' or '/webrev/'
  2473     # then add '/webrev'. This is for backward compatibility
  2474     if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
  2475     then
  2476 	WDIR=$WDIR/webrev
  2477     fi
  2478 fi
  2479 # WDIR=${WDIR:-$CWS/webrev}
  2482 # Name of the webrev, derived from the workspace name; in the
  2483 # future this could potentially be an option.
  2485 # Let's keep what's after the last '/'
  2486 WNAME=${CWS##*/}
  2489 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
  2491 if [ ${WDIR%%/*} ]; then
  2492     if [[ -n $ISWIN ]]; then
  2493         if [ ${WDIR%%[A-Za-z]:*} ]; then
  2494 	    WDIR=$PWD/$WDIR
  2495         fi
  2496     else
  2497 	WDIR=$PWD/$WDIR
  2498     fi
  2499 fi
  2501 if [[ ! -d $WDIR ]]; then
  2502 	mkdir -p $WDIR
  2503 	[[ $? != 0 ]] && exit 1
  2504 fi
  2507 # Summarize what we're going to do.
  2509 print "      Workspace: $CWS"
  2510 if [[ -n $parent_webrev ]]; then
  2511     print "Compare against: webrev at $parent_webrev"
  2512 elif [[ -n $OUTPWS2 ]]; then
  2513     print "Compare against: $OUTPWS2"
  2514 fi
  2515 if [[ -n $HG_BRANCH ]]; then
  2516     print "         Branch: $HG_BRANCH"
  2517 fi
  2518 if [[ -n $rflag ]]; then
  2519         print "Compare against version: $PARENT_REV"
  2520 fi
  2521 [[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
  2522 print "      Output to: $WDIR"
  2525 # Save the file list in the webrev dir
  2527 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
  2530 #    Bug IDs will be replaced by a URL.  Order of precedence
  2531 #    is: default location, $WEBREV_BUGURL, the -O flag.
  2533 BUGURL='https://jbs.oracle.com/bugs/browse/'
  2534 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  2535 if [[ -n "$Oflag" ]]; then
  2536     CRID=`echo $CRID | sed -e 's/JDK-//'`
  2537     BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
  2538     IDPREFIX=''
  2539 else
  2540     IDPREFIX='JDK-'
  2541 fi
  2545 #    Likewise, ARC cases will be replaced by a URL.  Order of precedence
  2546 #    is: default, $WEBREV_SACURL, the -O flag.
  2548 #    Note that -O also triggers different substitution behavior for
  2549 #    SACURL.  See sac2url().
  2551 SACURL='http://sac.eng.sun.com'
  2552 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
  2553 [[ -n $Oflag ]] && \
  2554     SACURL='http://www.opensolaris.org/os/community/arc/caselog'
  2556 rm -f $WDIR/$WNAME.patch
  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 [[ -v NO_OUTGOING ]];
  2787             then
  2788               # Only need to generate a patch file here if there are no commits in outgoing
  2789               rm -f $WDIR/$DIR/$F.patch
  2790               if [[ -z $rename ]]; then
  2791                   if [ ! -f $ofile ]; then
  2792                       diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2793                           > $WDIR/$DIR/$F.patch
  2794                   elif [ ! -f $nfile ]; then
  2795                       diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2796                           > $WDIR/$DIR/$F.patch
  2797                   else
  2798                       diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  2799                   fi
  2800               else
  2801                   diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2802                       > $WDIR/$DIR/$F.patch
  2804                   diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2805                       >> $WDIR/$DIR/$F.patch
  2807               fi
  2811             # Tack the patch we just made onto the accumulated patch for the
  2812             # whole wad.
  2814               cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  2815             fi
  2817             print " patch\c"
  2819 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2821 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  2822 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2823 		    > $WDIR/$DIR/$F.cdiff.html
  2824 		print " cdiffs\c"
  2826 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  2827 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2828 		    > $WDIR/$DIR/$F.udiff.html
  2830 		print " udiffs\c"
  2832 		if [[ -x $WDIFF ]]; then
  2833 		    $WDIFF -c "$COMM" \
  2834 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  2835 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2836 		    if [[ $? -eq 0 ]]; then
  2837 			print " wdiffs\c"
  2838 		    else
  2839 			print " wdiffs[fail]\c"
  2840 		    fi
  2841 		fi
  2843 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2844 		    > $WDIR/$DIR/$F.sdiff.html
  2845 		print " sdiffs\c"
  2847 		print " frames\c"
  2849 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2851 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2853 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2854 		# renamed file: may also have differences
  2855 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2856 	    elif [[ -f $nfile ]]; then
  2857 		# new file: count added lines
  2858 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  2859 	    elif [[ -f $ofile ]]; then
  2860 		# old file: count deleted lines
  2861 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  2862 	    fi
  2863 	fi
  2865 	# Now we generate the postscript for this file.  We generate diffs
  2866 	# only in the event that there is delta, or the file is new (it seems
  2867 	# tree-killing to print out the contents of deleted files).
  2869 	if [[ -f $nfile ]]; then
  2870 		ocr=$ofile
  2871 		[[ ! -f $ofile ]] && ocr=/dev/null
  2873 		if [[ -z $mv_but_nodiff ]]; then
  2874 			textcomm=`getcomments text $P $PP`
  2875 			if [[ -x $CODEREVIEW ]]; then
  2876 				$CODEREVIEW -y "$textcomm" \
  2877 				    -e $ocr $nfile \
  2878 				    > /tmp/$$.psfile 2>/dev/null &&
  2879 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  2880 				if [[ $? -eq 0 ]]; then
  2881 					print " ps\c"
  2882 				else
  2883 					print " ps[fail]\c"
  2884 				fi
  2885 			fi
  2886 		fi
  2887 	fi
  2889 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  2890 	    if [[ -n $its_a_jar ]]; then
  2891 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  2892 	    else
  2893 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  2894 	    fi
  2895 		print " old\c"
  2896 	fi
  2898 	if [[ -f $nfile ]]; then
  2899 	    if [[ -n $its_a_jar ]]; then
  2900 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  2901 	    else
  2902 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  2903 	    fi
  2904 		print " new\c"
  2905 	fi
  2907 	print
  2908 done < $FLIST
  2910 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
  2911 if [[ $SCM_MODE == "mercurial" ]]; then
  2912   if [[ !(-v NO_OUTGOING) ]]; then
  2913     EXPORTCHANGESET="$WNAME.changeset"
  2914     CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
  2915     rm -f $CHANGESETPATH
  2916     touch $CHANGESETPATH
  2917     if [[ -n $ALL_CREV ]]; then
  2918       rev_opt=
  2919       for rev in $ALL_CREV; do
  2920         rev_opt="$rev_opt --rev $rev"
  2921       done
  2922     elif [[ -n $FIRST_CREV ]]; then
  2923       rev_opt="--rev $FIRST_CREV"
  2924     fi
  2926     if [[ -n $rev_opt ]]; then
  2927       (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
  2928       # echo "Created new-patch: $CHANGESETPATH" 1>&2
  2929       # Use it in place of the jdk.patch created above
  2930       rm -f $WDIR/$WNAME.patch
  2931     fi
  2932   set +x
  2933   fi
  2934 fi
  2936 frame_nav_js > $WDIR/ancnav.js
  2937 frame_navigation > $WDIR/ancnav.html
  2939 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  2940 	print " Generating PDF: \c"
  2941 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  2942 	print "Done."
  2943 fi
  2945 # Now build the index.html file that contains
  2946 # links to the source files and their diffs.
  2948 cd $CWS
  2950 # Save total changed lines for Code Inspection.
  2951 print "$TOTL" > $WDIR/TotalChangedLines
  2953 print "     index.html: \c"
  2954 INDEXFILE=$WDIR/index.html
  2955 exec 3<&1			# duplicate stdout to FD3.
  2956 exec 1<&-			# Close stdout.
  2957 exec > $INDEXFILE		# Open stdout to index file.
  2959 print "$HTML<head>"
  2960 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  2961 print "$STDHEAD"
  2962 print "<title>$WNAME</title>"
  2963 print "</head>"
  2964 print "<body id=\"SUNWwebrev\">"
  2965 print "<div class=\"summary\">"
  2966 print "<h2>Code Review for $WNAME</h2>"
  2968 print "<table>"
  2970 if [[ -z $uflag ]]
  2971 then
  2972     if [[ $SCM_MODE == "mercurial" ]]
  2973     then
  2975         # Let's try to extract the user name from the .hgrc file
  2977 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  2978     fi
  2980     if [[ -z $username ]]
  2981     then
  2983         # Figure out the username and gcos name.  To maintain compatibility
  2984         # with passwd(4), we must support '&' substitutions.
  2986 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  2987 	if [[ -x $GETENT ]]; then
  2988 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  2989 	fi
  2990 	userupper=`print "$username" | sed 's/\<./\u&/g'`
  2991 	realname=`print $realname | sed s/\&/$userupper/`
  2992     fi
  2993 fi
  2995 date="on `date`"
  2997 if [[ -n "$username" && -n "$realname" ]]; then
  2998 	print "<tr><th>Prepared by:</th>"
  2999 	print "<td>$realname ($username) $date</td></tr>"
  3000 elif [[ -n "$username" ]]; then
  3001 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  3002 fi
  3004 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  3005 if [[ -n $parent_webrev ]]; then
  3006         print "<tr><th>Compare against:</th><td>"
  3007 	print "webrev at $parent_webrev"
  3008 else
  3009     if [[ -n $OUTPWS2 ]]; then
  3010         print "<tr><th>Compare against:</th><td>"
  3011 	print "$OUTPWS2"
  3012     fi
  3013 fi
  3014 print "</td></tr>"
  3015 if [[ -n $rflag ]]; then
  3016     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  3017 elif [[ -n $OUTREV ]]; then
  3018     if [[ -z $forestflag ]]; then
  3019         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  3020     fi
  3021 fi
  3022 if [[ -n $HG_BRANCH ]]; then
  3023     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  3024 fi
  3026 print "<tr><th>Summary of changes:</th><td>"
  3027 printCI $TOTL $TINS $TDEL $TMOD $TUNC
  3028 print "</td></tr>"
  3030 if [[ -f $WDIR/$WNAME.patch ]]; then
  3031   print "<tr><th>Patch of changes:</th><td>"
  3032   print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  3033 elif [[ -f $CHANGESETPATH ]]; then
  3034   print "<tr><th>Changeset:</th><td>"
  3035   print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
  3036 fi
  3038 if [[ -f $WDIR/$WNAME.pdf ]]; then
  3039 	print "<tr><th>Printable review:</th><td>"
  3040 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  3041 fi
  3043 if [[ -n "$iflag" ]]; then
  3044 	print "<tr><th>Author comments:</th><td><div>"
  3045 	cat /tmp/$$.include
  3046 	print "</div></td></tr>"
  3047 fi
  3048 # Add links to referenced CRs, if any
  3049 # external URL has a <title> like:
  3050 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
  3051 # while internal URL has <title> like:
  3052 # <title>[#JDK-6641309] Wrong Cookie separator used in HttpURLConnection</title>
  3054 if [[ -n $CRID ]]; then
  3055     for id in $CRID
  3056     do
  3057         if [[ -z "$Oflag" ]]; then
  3058             #add "JDK-" to raw bug id for jbs links.
  3059             id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
  3060         fi
  3061         print "<tr><th>Bug id:</th><td>"
  3062         url="${BUGURL}${id}"
  3063         if [[ -n "$Oflag" ]]; then
  3064             cleanup='s/Bug ID: \([0-9]\{5,\}\) \(.*\)/JDK-\1 : \2/'
  3065         else
  3066             cleanup='s|\[#\(JDK-[0-9]\{5,\}\)\] \(.*\)|\1 : \2|'
  3067         fi
  3068         if [[ -n $WGET ]]; then
  3069             msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed "$cleanup" | html_quote`
  3070         fi
  3071         if [[ -z $msg ]]; then
  3072             msg="${id}"
  3073         fi
  3075         print "<a href=\"$url\">$msg</a>"
  3077         print "</td></tr>"
  3078     done
  3079 fi
  3080 print "<tr><th>Legend:</th><td>"
  3081 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>"
  3082 print "</table>"
  3083 print "</div>"
  3086 # Second pass through the files: generate the rest of the index file
  3088 while read LINE
  3089 do
  3090 	set - $LINE
  3091         if [[ $1 == "Revision:" ]]; then
  3092             FIRST_CREV=`expr $3 + 1`
  3093             continue
  3094         fi
  3095 	P=$1
  3097 	if [[ $# == 2 ]]; then
  3098 		PP=$2
  3099 		oldname=" <i>(was $PP)</i>"
  3101 	else
  3102 		PP=$P
  3103 		oldname=""
  3104 	fi
  3106 	DIR=${P%/*}
  3107 	if [[ $DIR == $P ]]; then
  3108 		DIR="."   # File at root of workspace
  3109 	fi
  3111 	# Avoid processing the same file twice.
  3112 	# It's possible for renamed files to
  3113 	# appear twice in the file list
  3115 	F=$WDIR/$P
  3117 	print "<p><code>"
  3119 	# If there's a diffs file, make diffs links
  3121         NODIFFS=
  3122 	if [[ -f $F.cdiff.html ]]; then
  3123 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  3124 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  3126 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  3127 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  3128 		fi
  3130 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  3132 		print "<a href=\"$P.frames.html\">Frames</a>"
  3133 	else
  3134                 NODIFFS=1
  3135 		print " ------ ------ ------"
  3137 		if [[ -x $WDIFF ]]; then
  3138 			print " ------"
  3139 		fi
  3141 		print " ------"
  3142 	fi
  3144 	# If there's an old file, make the link
  3146         NOOLD=
  3147 	if [[ -f $F-.html ]]; then
  3148 		print "<a href=\"$P-.html\">Old</a>"
  3149 	else
  3150                 NOOLD=1
  3151 		print " ---"
  3152 	fi
  3154 	# If there's an new file, make the link
  3156         NONEW=
  3157 	if [[ -f $F.html ]]; then
  3158 		print "<a href=\"$P.html\">New</a>"
  3159 	else
  3160                 NONEW=1
  3161 		print " ---"
  3162 	fi
  3164 	if [[ -f $F.patch ]]; then
  3165 		print "<a href=\"$P.patch\">Patch</a>"
  3166 	else
  3167 		print " -----"
  3168 	fi
  3170 	if [[ -f $WDIR/raw_files/new/$P ]]; then
  3171 		print "<a href=\"raw_files/new/$P\">Raw</a>"
  3172 	else
  3173 		print " ---"
  3174 	fi
  3175         print "</code>"
  3176         if [[ -n $NODIFFS && -z $oldname ]]; then
  3177             if [[ -n $NOOLD ]]; then
  3178                 print "<font color=green><b>$P</b></font>"
  3179             elif [[ -n $NONEW ]]; then
  3180                 print "<font color=red><b>$P</b></font>"
  3181             fi
  3182         else
  3183 	    print "<b>$P</b> $oldname"
  3184         fi
  3187 	# Check for usr/closed
  3189 	if [ ! -z "$Oflag" ]; then
  3190 		if [[ $P == usr/closed/* ]]; then
  3191 			print "&nbsp;&nbsp;<i>Closed source: omitted from" \
  3192 			    "this review</i>"
  3193 		fi
  3194 	fi
  3196 	print "</p><blockquote>\c"
  3197 	# Insert delta comments if any
  3198 	comments=`getcomments html $P $PP`
  3199 	if [ -n "$comments" ]; then
  3200 	    print "<pre>$comments</pre>"
  3201 	fi
  3203 	# Add additional comments comment
  3205 	print "<!-- Add comments to explain changes in $P here -->"
  3207 	# Add count of changes.
  3209 	if [[ -f $F.count ]]; then
  3210 	    cat $F.count
  3211 	    rm $F.count
  3212 	fi
  3213         print "</blockquote>"
  3214 done < $FLIST
  3216 print
  3217 print
  3218 print "<hr />"
  3219 print "<p style=\"font-size: small\">"
  3220 print "This code review page was prepared using <b>$0</b>"
  3221 print "(vers $WEBREV_UPDATED)."
  3222 print "</body>"
  3223 print "</html>"
  3225 if [[ -n $ZIP ]]; then
  3226     # Let's generate a zip file for convenience
  3227     cd $WDIR/..
  3228     if [ -f webrev.zip ]; then
  3229 	rm webrev.zip
  3230     fi
  3231     $ZIP -r webrev webrev >/dev/null 2>&1
  3232 fi
  3234 exec 1<&-			# Close FD 1.
  3235 exec 1<&3			# dup FD 3 to restore stdout.
  3236 exec 3<&-			# close FD 3.
  3238 print "Done."
  3239 print "Output to: $WDIR"

mercurial