make/scripts/webrev.ksh

Wed, 20 Feb 2013 17:56:16 -0800

author
mduigou
date
Wed, 20 Feb 2013 17:56:16 -0800
changeset 643
c641268c4532
parent 616
168dd033604a
child 678
26a4456cb19e
permissions
-rw-r--r--

8008629: webrev.ksh needs to quote bug title it gets back from scraping bugs.sun.com
Reviewed-by: darcy

     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 	print " File list from hg fstatus -mard ...\c"
  1447         FSTAT_OPT=
  1448         fstatus
  1449         HG_LIST_FROM_COMMIT=0
  1450     fi
  1451     print " Done."
  1455 # Used when dealing with the result of 'hg foutgoing'
  1456 # When now go down the tree and generate the change list
  1458 function treestatus
  1460     TREE=$1
  1461     HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
  1463     $HGCMD -mdn 2>/dev/null | $FILTER | while read F
  1464     do
  1465         echo $TREE/$F
  1466     done >> $FLIST
  1468     # Then all the added files
  1469     # But some of these could have been "moved" or renamed ones
  1470     # so let's make sure we get the proper info
  1471     # hg status -aC will produce something like:
  1472     #	A subdir/File3
  1473     #	A subdir/File4
  1474     #	  File4
  1475     #	A subdir/File5
  1476     # The first and last are simple addition while the middle one
  1477     # is a move/rename
  1479     $HGCMD -aC | $FILTER | while read LINE; do
  1480 	ldone=""
  1481 	while [ -z "$ldone" ]; do
  1482 	    ldone="1"
  1483 	    set - $LINE
  1484 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1485 		AFILE=$2
  1486 		if read LINE2; then
  1487 		    set - $LINE2
  1488 		    if [ $# -eq 1 ]; then
  1489 			echo $TREE/$AFILE $TREE/$1 >>$FLIST
  1490 		    elif [ $# -eq 2 ]; then
  1491 			echo $TREE/$AFILE >>$FLIST
  1492 			LINE=$LINE2
  1493 			ldone=""
  1494 		    fi
  1495 		else
  1496 		    echo $TREE/$AFILE >>$FLIST
  1497 		fi
  1498 	    fi
  1499 	done
  1500     done
  1501     $HGCMD -rn | $FILTER | while read RFILE; do
  1502 	grep "$TREE/$RFILE" $FLIST >/dev/null
  1503 	if [ $? -eq 1 ]; then
  1504 	    echo $TREE/$RFILE >>$FLIST
  1505 	fi
  1506     done
  1509 function fstatus
  1512     # forest extension is still being changed. For instance the output
  1513     # of fstatus used to no prepend the tree path to filenames, but
  1514     # this has changed recently. AWK code below does try to handle both
  1515     # cases
  1517     hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1518 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1519 	$1 != ""	{n=index($1,tree);
  1520 			 if (n == 0)
  1521 				{ printf("%s/%s\n",tree,$1)}
  1522 			 else
  1523 				{ printf("%s\n",$1)}}' >> $FLIST
  1526     # There is a bug in the output of fstatus -aC on recent versions: it
  1527     # inserts a space between the name of the tree and the filename of the
  1528     # old file. e.g.:
  1530     # $ hg fstatus -aC
  1531     # [.]
  1533     # [MyWS]
  1534     # A MyWS/subdir/File2
  1535     #  MyWS/ File2
  1537     # [MyWS2]
  1540     hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1541 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1542 	/^A .*/		{n=index($2,tree);
  1543 			 if (n == 0)
  1544 				{ printf("A %s/%s\n",tree,$2)}
  1545 			 else
  1546 				{ printf("A %s\n",$2)};
  1547 			 next}
  1548 	/^ /		{n=index($1,tree);
  1549 			 if (n == 0)
  1550 				{ printf("%s/%s\n",tree,$1)}
  1551 			 else
  1552 				{ if (NF == 2)
  1553 					printf("%s/%s\n",tree,$2)
  1554 				  else
  1555 					printf("%s\n",$1)
  1556 				};
  1557 			 next}
  1558 	' | while read LINE; do
  1559 	ldone=""
  1560 	while [ -z "$ldone" ]; do
  1561 	    ldone="1"
  1562 	    set - $LINE
  1563 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1564 		AFILE=$2
  1565 		if read LINE2; then
  1566 		    set - $LINE2
  1567 		    if [ $# -eq 1 ]; then
  1568 			echo $AFILE $1 >>$FLIST
  1569 		    elif [ $# -eq 2 ]; then
  1570 			echo $AFILE >>$FLIST
  1571 			LINE=$LINE2
  1572 			ldone=""
  1573 		    fi
  1574 		else
  1575 		    echo $AFILE >>$FLIST
  1576 		fi
  1577 	    fi
  1578 	done
  1579     done
  1580     hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1581 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1582 	$1 != ""	{n=index($1,tree);
  1583 			 if (n == 0)
  1584 				{ printf("%s/%s\n",tree,$1)}
  1585 			 else
  1586 				{ printf("%s\n",$1)}}' | while read RFILE; do
  1587 	grep "$RFILE" $FLIST >/dev/null
  1588 	if [ $? -eq 1 ]; then
  1589 	    echo $RFILE >>$FLIST
  1590 	fi
  1591     done
  1595 # flist_from_mercurial $PWS
  1597 # Only local file based repositories are supported at present
  1598 # since even though we can determine the list from the parent finding
  1599 # the changes is harder.
  1601 # We first look for any outgoing files, this is for when the user has
  1602 # run hg commit.  If we don't find any then we look with hg status.
  1604 # We need at least one of default-push or default paths set in .hg/hgrc
  1605 # If neither are set we don't know who to compare with.
  1607 function flist_from_mercurial
  1609 #	if [ "${PWS##ssh://}" != "$PWS" -o \
  1610 #	     "${PWS##http://}" != "$PWS" -o \
  1611 #	     "${PWS##https://}" != "$PWS" ]; then
  1612 #		print "Remote Mercurial repositories not currently supported."
  1613 #		print "Set default and/or default-push to a local repository"
  1614 #		exit
  1615 #	fi
  1616     if [[ -n $forestflag ]]; then
  1617         HG_LIST_FROM_COMMIT=
  1618 	flist_from_mercurial_forest
  1619     else
  1620         STATUS_REV=
  1621         if [[ -n $rflag ]]; then
  1622             STATUS_REV="--rev $PARENT_REV"
  1623         elif [[ -n $OUTREV ]]; then
  1624             STATUS_REV="--rev $OUTREV"
  1625         else
  1626             # hg commit hasn't been run see what is lying around
  1627             print "\n No outgoing, perhaps you haven't commited."
  1628         fi
  1629 	# First let's list all the modified or deleted files
  1631 	hg status $STATUS_REV -mdn | $FILTER > $FLIST
  1633 	# Then all the added files
  1634 	# But some of these could have been "moved" or renamed ones
  1635 	# so let's make sure we get the proper info
  1636 	# hg status -aC will produce something like:
  1637 	#	A subdir/File3
  1638 	#	A subdir/File4
  1639 	#	  File4
  1640 	#	A subdir/File5
  1641 	# The first and last are simple addition while the middle one
  1642 	# is a move/rename
  1644 	hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
  1645 	while read LINE; do
  1646 	    ldone=""
  1647 	    while [ -z "$ldone" ]; do
  1648 		ldone="1"
  1649 		set - $LINE
  1650 		if [ $# -eq 2 -a "$1" == "A" ]; then
  1651 		    AFILE=$2
  1652 		    if read LINE2; then
  1653 			set - $LINE2
  1654 			if [ $# -eq 1 ]; then
  1655 			    echo $AFILE $1 >>$FLIST
  1656 			elif [ $# -eq 2 ]; then
  1657 			    echo $AFILE >>$FLIST
  1658 			    LINE=$LINE2
  1659 			    ldone=""
  1660 			fi
  1661 		    else
  1662 			echo $AFILE >>$FLIST
  1663 		    fi
  1664 		fi
  1665 	    done
  1666 	done < $FLIST.temp
  1667 	hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
  1668 	while read RFILE; do
  1669 	    grep "$RFILE" $FLIST >/dev/null
  1670 	    if [ $? -eq 1 ]; then
  1671 		echo $RFILE >>$FLIST
  1672 	    fi
  1673 	done < $FLIST.temp
  1674 	rm -f $FLIST.temp
  1675     fi
  1678 function env_from_flist
  1680 	[[ -r $FLIST ]] || return
  1683 	# Use "eval" to set env variables that are listed in the file
  1684 	# list.  Then copy those into our local versions of those
  1685 	# variables if they have not been set already.
  1687 	eval `sed -e "s/#.*$//" $FLIST | grep = `
  1689 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  1692 	# Check to see if CODEMGR_PARENT is set in the flist file.
  1694 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  1695 	    codemgr_parent=$CODEMGR_PARENT
  1699 # detect_scm
  1701 # We dynamically test the SCM type; this allows future extensions to
  1702 # new SCM types
  1704 function detect_scm
  1707 	# If CODEMGR_WS is specified in the flist file, we assume teamware.
  1709 	if [[ -r $FLIST ]]; then
  1710 		egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
  1711 		if [[ $? -eq 0 ]]; then
  1712 			print "teamware"
  1713 			return
  1714 		fi
  1715 	fi
  1718 	# The presence of $CODEMGR_WS and a Codemgr_wsdata directory
  1719 	# is our clue that this is a teamware workspace.
  1720 	# Same if true if current directory has a Codemgr_wsdata sub-dir
  1722 	if [[ -z "$CODEMGR_WS" ]]; then
  1723 	    CODEMGR_WS=`workspace name 2>/dev/null`
  1724 	fi
  1726 	if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
  1727 		print "teamware"
  1728 	elif [[ -d $PWD/Codemgr_wsdata ]]; then
  1729 		print "teamware"
  1730 	elif hg root >/dev/null ; then
  1731 		print "mercurial"
  1732 	else
  1733 		print "unknown"
  1734 	fi
  1738 # Extract the parent workspace from the Codemgr_wsdata/parent file
  1740 function parent_from_teamware
  1742     if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
  1743 	tail -1 "$1/Codemgr_wsdata/parent"
  1744     fi
  1747 function look_for_prog
  1749 	typeset path
  1750 	typeset ppath
  1751 	typeset progname=$1
  1753 	DEVTOOLS=
  1754 	OS=`uname`
  1755 	if [[ "$OS" == "SunOS" ]]; then
  1756 	    DEVTOOLS="/java/devtools/`uname -p`/bin"
  1757 	elif [[ "$OS" == "Linux" ]]; then
  1758 	    DEVTOOLS="/java/devtools/linux/bin"
  1759 	fi
  1761 	ppath=$PATH
  1762 	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
  1763 	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
  1764 	ppath=$ppath:/opt/onbld/bin/`uname -p`
  1765 	ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
  1767 	PATH=$ppath prog=`whence $progname`
  1768 	if [[ -n $prog ]]; then
  1769 		print $prog
  1770 	fi
  1773 function build_old_new_teamware
  1775 	# If the child's version doesn't exist then
  1776 	# get a readonly copy.
  1778 	if [[ ! -f $F && -f SCCS/s.$F ]]; then
  1779 		$SCCS get -s $F
  1780 	fi
  1783 	# Snag new version of file.
  1785 	rm -f $newdir/$DIR/$F
  1786 	cp $F $newdir/$DIR/$F
  1789 	# Get the parent's version of the file. First see whether the
  1790 	# child's version is checked out and get the parent's version
  1791 	# with keywords expanded or unexpanded as appropriate.
  1793 	if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
  1794 	    -f $PWS/$PDIR/SCCS/p.$PF ]; then
  1795 		rm -f $olddir/$PDIR/$PF
  1796 		if [ -f SCCS/p.$F ]; then
  1797 			$SCCS get -s -p -k $PWS/$PDIR/$PF \
  1798 			    > $olddir/$PDIR/$PF
  1799 		else
  1800 			$SCCS get -s -p    $PWS/$PDIR/$PF \
  1801 			    > $olddir/$PDIR/$PF
  1802 		fi
  1803 	else
  1804 		if [[ -f $PWS/$PDIR/$PF ]]; then
  1805 			# Parent is not a real workspace, but just a raw
  1806 			# directory tree - use the file that's there as
  1807 			# the old file.
  1809 			rm -f $olddir/$DIR/$F
  1810 			cp $PWS/$PDIR/$PF $olddir/$DIR/$F
  1811 		fi
  1812 	fi
  1816 # Find the parent for $1
  1818 function find_outrev
  1820     crev=$1
  1821     prev=`hg log -r $crev --template '{parents}\n'`
  1822     if [[ -z "$prev" ]]
  1823     then
  1824 	# No specific parent means previous changeset is parent
  1825 	prev=`expr $crev - 1`
  1826     else
  1827 	# Format is either of the following two:
  1828 	# 546:7df6fcf1183b
  1829 	# 548:16f1915bb5cd 547:ffaa4e775815
  1830 	prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
  1831     fi
  1832     print $prev
  1835 function extract_ssh_infos
  1837     CMD=$1
  1838     if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
  1839 	ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
  1840 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
  1841 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
  1842     else
  1843 	ssh_user=
  1844 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
  1845 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
  1846     fi
  1850 function build_old_new_mercurial
  1852 	olddir=$1
  1853 	newdir=$2
  1854 	DIR=$3
  1855 	F=$4
  1857 	# new version of the file.
  1859 	rm -rf $newdir/$DIR/$F
  1860 	if [ -f $F ]; then
  1861 	    cp $F  $newdir/$DIR/$F
  1862 	fi
  1865 	# Old version of the file.
  1867 	rm -rf $olddir/$DIR/$F
  1869 	if [ -n "$PWS" ]; then
  1870 	    if expr "$PWS" : 'ssh://' >/dev/null
  1871 	    then
  1872 		extract_ssh_infos $PWS
  1873 		if [ -n "$ssh_user" ]; then
  1874 		    parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1875 		else
  1876 		    parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1877 		fi
  1878 	    else
  1879 		parent="hg -R $PWS --cwd $PWS"
  1880 	    fi
  1881 	else
  1882 	    parent=""
  1883 	fi
  1885 	if [ -z "$rename" ]; then
  1886 	    if [ -n "$rflag" ]; then
  1887 		parentrev=$PARENT_REV
  1888 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1889                 parentrev=$OUTREV
  1890 	    else
  1891                 if [[ -n $HG_BRANCH ]]; then
  1892                     parentrev=$HG_BRANCH
  1893                 else
  1894 		    parentrev="tip"
  1895                 fi
  1896 	    fi
  1898 	    if [ -n "$parentrev" ]; then
  1899 		if [ -z "$parent" ]; then
  1900 		    hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
  1901 		else
  1902 		    # when specifying a workspace we have to provide
  1903 		    # the full path
  1904 		    $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
  1905 		fi
  1906 	    fi
  1907 	else
  1908 	    # It's a rename (or a move), so let's make sure we move
  1909 	    # to the right directory first, then restore it once done
  1910 	    current_dir=`pwd`
  1911 	    cd $CWS/$PDIR
  1912 	    if [ -n "$rflag" ]; then
  1913 		parentrev=$PARENT_REV
  1914 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1915                 parentrev=$OUTREV
  1916 	    fi
  1917 	    if [ -z "$parentrev" ]; then
  1918 		parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
  1919 	    fi
  1920 	    if [ -n "$parentrev" ]; then
  1921 		mkdir -p $olddir/$PDIR
  1922 		if [ -z "$parent" ]; then
  1923 		    hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
  1924 		else
  1925 		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1926 		fi
  1927 	    fi
  1928 	    cd $current_dir
  1929 	fi
  1932 function build_old_new
  1934 	if [[ $SCM_MODE == "teamware" ]]; then
  1935 		build_old_new_teamware $@
  1936 	fi
  1938 	if [[ $SCM_MODE == "mercurial" ]]; then
  1939 		build_old_new_mercurial $@
  1940 	fi
  1945 # Usage message.
  1947 function usage
  1949 	print "Usage:\twebrev [common-options]
  1950 	webrev [common-options] ( <file> | - )
  1951 	webrev [common-options] -w <wx file>
  1952 	webrev [common-options] -l [arguments to 'putback']
  1954 Options:
  1955 	-v: Print the version of this tool.
  1956         -b: Do not ignore changes in the amount of white space.
  1957         -c <CR#>: Include link to CR (aka bugid) in the main page.
  1958 	-O: Print bugids/arc cases suitable for OpenJDK.
  1959 	-i <filename>: Include <filename> in the index.html file.
  1960 	-o <outdir>: Output webrev to specified directory.
  1961 	-p <compare-against>: Use specified parent wkspc or basis for comparison
  1962 	-w <wxfile>: Use specified wx active file.
  1963         -u <username>: Use that username instead of 'guessing' one.
  1964 	-m: Forces the use of Mercurial
  1965 	-t: Forces the use of Teamware
  1967 Mercurial only options:
  1968 	-r rev: Compare against a specified revision
  1969 	-N: Skip 'hg outgoing', use only 'hg status'
  1970 	-f: Use the forest extension
  1972 Environment:
  1973 	WDIR: Control the output directory.
  1974 	WEBREV_BUGURL: Control the URL prefix for bugids.
  1975 	WEBREV_SACURL: Control the URL prefix for ARC cases.
  1977 SCM Environment:
  1978 	Teamware: CODEMGR_WS: Workspace location.
  1979 	Teamware: CODEMGR_PARENT: Parent workspace location.
  1983 	exit 2
  1988 # Main program starts here
  1991 LANG="C"
  1992 LC_ALL="C"
  1993 export LANG LC_ALL
  1994 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
  1996 set +o noclobber
  1998 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
  1999 [[ -z $WX ]] && WX=`look_for_prog wx`
  2000 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
  2001 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
  2002 [[ -z $PERL ]] && PERL=`look_for_prog perl`
  2003 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
  2004 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
  2005 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
  2006 [[ -z $AWK ]] && AWK=`look_for_prog awk`
  2007 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
  2008 [[ -z $JAR ]] && JAR=`look_for_prog jar`
  2009 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
  2010 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
  2011 [[ -z $WGET ]] && WGET=`look_for_prog wget`
  2013 if uname | grep CYGWIN >/dev/null
  2014 then
  2015         ISWIN=1
  2016         # Under windows mercurial outputs '\' instead of '/'
  2017         FILTER="tr '\\\\' '/'"
  2018 else
  2019         FILTER="cat"
  2020 fi
  2022 if [[ ! -x $PERL ]]; then
  2023 	print -u2 "Error: No perl interpreter found.  Exiting."
  2024 	exit 1
  2025 fi
  2028 # These aren't fatal, but we want to note them to the user.
  2029 # We don't warn on the absence of 'wx' until later when we've
  2030 # determined that we actually need to try to invoke it.
  2032 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
  2033 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
  2034 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
  2036 # Declare global total counters.
  2037 integer TOTL TINS TDEL TMOD TUNC
  2039 flist_mode=
  2040 flist_file=
  2041 bflag=
  2042 iflag=
  2043 oflag=
  2044 pflag=
  2045 uflag=
  2046 lflag=
  2047 wflag=
  2048 Oflag=
  2049 rflag=
  2050 Nflag=
  2051 forestflag=
  2052 while getopts "c:i:o:p:r:u:lmtwONvfb" opt
  2053 do
  2054 	case $opt in
  2055         b)      bflag=1;;
  2057 	i)	iflag=1
  2058 		INCLUDE_FILE=$OPTARG;;
  2060 	o)	oflag=1
  2061 		WDIR=$OPTARG;;
  2063 	p)	pflag=1
  2064 		codemgr_parent=$OPTARG;;
  2066 	u)      uflag=1
  2067 		username=$OPTARG;;
  2069         c)      if [[ -z $CRID ]]; then
  2070                    CRID=$OPTARG
  2071                 else
  2072                    CRID="$CRID $OPTARG"
  2073                 fi;;
  2075 	m)	SCM_MODE="mercurial";;
  2077 	t)	SCM_MODE="teamware";;
  2080 	# If -l has been specified, we need to abort further options
  2081 	# processing, because subsequent arguments are going to be
  2082 	# arguments to 'putback -n'.
  2084 	l)	lflag=1
  2085 		break;;
  2087 	w)	wflag=1;;
  2089 	O)	Oflag=1;;
  2091 	N)	Nflag=1;;
  2093 	f)	forestflag=1;;
  2095 	r)	rflag=1
  2096 		PARENT_REV=$OPTARG;;
  2098 	v)	print "$0 version: $WEBREV_UPDATED";;
  2101 	?)	usage;;
  2102 	esac
  2103 done
  2105 FLIST=/tmp/$$.flist
  2107 if [[ -n $wflag && -n $lflag ]]; then
  2108 	usage
  2109 fi
  2111 if [[ -n $forestflag && -n $rflag ]]; then
  2112     print "The -r <rev> flag is incompatible with the use of forests"
  2113     exit 2
  2114 fi
  2117 # If this manually set as the parent, and it appears to be an earlier webrev,
  2118 # then note that fact and set the parent to the raw_files/new subdirectory.
  2120 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
  2121 	parent_webrev="$codemgr_parent"
  2122 	codemgr_parent="$codemgr_parent/raw_files/new"
  2123 fi
  2125 if [[ -z $wflag && -z $lflag ]]; then
  2126 	shift $(($OPTIND - 1))
  2128 	if [[ $1 == "-" ]]; then
  2129 		cat > $FLIST
  2130 		flist_mode="stdin"
  2131 		flist_done=1
  2132 		shift
  2133 	elif [[ -n $1 ]]; then
  2134 		if [[ ! -r $1 ]]; then
  2135 			print -u2 "$1: no such file or not readable"
  2136 			usage
  2137 		fi
  2138 		cat $1 > $FLIST
  2139 		flist_mode="file"
  2140 		flist_file=$1
  2141 		flist_done=1
  2142 		shift
  2143 	else
  2144 		flist_mode="auto"
  2145 	fi
  2146 fi
  2149 # Before we go on to further consider -l and -w, work out which SCM we think
  2150 # is in use.
  2152 if [[ -z $SCM_MODE ]]; then
  2153     SCM_MODE=`detect_scm $FLIST`
  2154 fi
  2155 if [[ $SCM_MODE == "unknown" ]]; then
  2156 	print -u2 "Unable to determine SCM type currently in use."
  2157 	print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
  2158 	print -u2 "              the environment or in the file list."
  2159 	print -u2 "For mercurial: webrev runs 'hg root'."
  2160 	exit 1
  2161 fi
  2163 print -u2 "   SCM detected: $SCM_MODE"
  2166 if [[ $SCM_MODE == "mercurial" ]]; then
  2168     # determine Workspace and parent workspace paths
  2170     CWS=`hg root | $FILTER`
  2171     if [[ -n $pflag && -z "$PWS" ]]; then
  2172 	OUTPWS=$codemgr_parent
  2173         # Let's try to expand it if it's an alias defined in [paths]
  2174         tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  2175         if [[ -n $tmp ]]; then
  2176             OUTPWS="$tmp"
  2177         fi
  2178         if [[ -n $rflag ]]; then
  2179 	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
  2180 	        PWS=$codemgr_parent
  2181 	    else
  2182 	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
  2183 	    fi
  2184         fi
  2185     fi
  2187     # OUTPWS is the parent repository to use when using 'hg outgoing'
  2189     if [[ -z $Nflag ]]; then
  2190         if [[ -n $forestflag ]]; then
  2192             # for forest we have to rely on properly set default and
  2193             # default-push because they can be different from the top one.
  2194             # unless of course it was explicitely speficied with -p
  2195             if [[ -z $pflag ]]; then
  2196                 OUTPWS=
  2197             fi
  2198         else
  2200             # Unfortunately mercurial is bugged and doesn't handle
  2201             # aliases correctly in 'hg path default'
  2202             # So let's do it ourselves. Sigh...
  2203             if [[ -z "$OUTPWS" ]]; then
  2204                 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  2205             fi
  2206             # Still empty, means no default-push
  2207             if [[ -z "$OUTPWS" ]]; then
  2208                 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  2209             fi
  2210             # Let's try to expand it if it's an alias defined in [paths]
  2211             tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  2212             if [[ -n $tmp ]]; then
  2213                 OUTPWS="$tmp"
  2214             fi
  2215         fi
  2216     fi
  2218     # OUTPWS may contain username:password, let's make sure we remove the
  2219     # sensitive information before we print out anything in the HTML
  2221     OUTPWS2=$OUTPWS
  2222     if [[ -n $OUTPWS ]]; then
  2223 	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
  2224 	    # Remove everything between '://' and '@'
  2225 	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
  2226 	fi
  2227     fi
  2229     if [[ -z $HG_BRANCH ]]; then
  2230         HG_BRANCH=`hg branch`
  2231         if [ "$HG_BRANCH" == "default" ]; then
  2233             # 'default' means no particular branch, so let's cancel that
  2235             HG_BRANCH=
  2236         fi
  2237     fi
  2239     if [[ -z $forestflag ]]; then
  2240         if [[ -z $Nflag ]]; then
  2242             # If no "-N", always do "hg outgoing" against parent
  2243             # repository to determine list of outgoing revisions.
  2245             ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
  2246             if [[ -n $ALL_CREV ]]; then
  2247                 FIRST_CREV=`echo "$ALL_CREV" | head -1`
  2249                 # If no "-r", choose revision to compare against by
  2250                 # finding the latest revision not in the outgoing list.
  2252                 if [[ -z $rflag ]]; then
  2253                     OUTREV=`find_outrev "$FIRST_CREV"`
  2254                     if [[ -n $OUTREV ]]; then
  2255                         HG_LIST_FROM_COMMIT=1
  2256                     fi
  2257                 fi
  2258             fi
  2259         elif [[ -n $rflag ]]; then
  2261             # If skipping "hg outgoing" but still comparing against a
  2262             # specific revision (not the tip), set revision for comment
  2263             # accumulation.
  2265             FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
  2266             FIRST_CREV=`expr $FIRST_CREV + 1`
  2267         fi
  2268     fi
  2269     #Let's check if a merge is needed, if so, issue a warning
  2270     PREV=`hg parent | grep '^tag:.*tip$'`
  2271     if [[ -z $PREV ]]; then
  2272         print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
  2273     fi
  2274 fi
  2276 if [[ -n $lflag ]]; then
  2278 	# If the -l flag is given instead of the name of a file list,
  2279 	# then generate the file list by extracting file names from a
  2280 	# putback -n.
  2282 	shift $(($OPTIND - 1))
  2283 	if [[ $SCM_MODE == "teamware" ]]; then
  2284 		flist_from_teamware "$*"
  2285 	elif [[ $SCM_MODE == "mercurial" ]]; then
  2286 		flist_from_mercurial
  2287 	fi
  2288 	flist_done=1
  2289 	shift $#
  2291 elif [[ -n $wflag ]]; then
  2293 	# If the -w is given then assume the file list is in Bonwick's "wx"
  2294 	# command format, i.e.  pathname lines alternating with SCCS comment
  2295 	# lines with blank lines as separators.  Use the SCCS comments later
  2296 	# in building the index.html file.
  2298 	shift $(($OPTIND - 1))
  2299 	wxfile=$1
  2300 	if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
  2301 		if [[ -r $CODEMGR_WS/wx/active ]]; then
  2302 			wxfile=$CODEMGR_WS/wx/active
  2303 		fi
  2304 	fi
  2306 	[[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
  2307 	    "be auto-detected (check \$CODEMGR_WS)" && exit 1
  2309 	print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
  2310 	flist_from_wx $wxfile
  2311 	flist_done=1
  2312 	if [[ -n "$*" ]]; then
  2313 		shift
  2314 	fi
  2315 elif [[ $flist_mode == "stdin" ]]; then
  2316 	print -u2 " File list from: standard input"
  2317 elif [[ $flist_mode == "file" ]]; then
  2318 	print -u2 " File list from: $flist_file"
  2319 fi
  2321 if [[ $# -gt 0 ]]; then
  2322 	print -u2 "WARNING: unused arguments: $*"
  2323 fi
  2325 if [[ $SCM_MODE == "teamware" ]]; then
  2327 	# Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
  2328 	# be set in a number of ways, in decreasing precedence:
  2330 	#      1) on the command line (only for the parent)
  2331 	#      2) in the user environment
  2332 	#      3) in the flist
  2333 	#      4) automatically based on the workspace (only for the parent)
  2337 	# Here is case (2): the user environment
  2339 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  2340 	[[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
  2342 	if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
  2343 		print -u2 "$codemgr_ws: no such workspace"
  2344 		exit 1
  2345 	fi
  2347 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  2348 	    codemgr_parent=$CODEMGR_PARENT
  2350 	if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
  2351 		print -u2 "$codemgr_parent: no such directory"
  2352 		exit 1
  2353 	fi
  2356 	# If we're in auto-detect mode and we haven't already gotten the file
  2357 	# list, then see if we can get it by probing for wx.
  2359 	if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
  2360 		if [[ ! -x $WX ]]; then
  2361 			print -u2 "WARNING: wx not found!"
  2362 		fi
  2365 		# We need to use wx list -w so that we get renamed files, etc.
  2366 		# but only if a wx active file exists-- otherwise wx will
  2367 		# hang asking us to initialize our wx information.
  2369 		if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
  2370 			print -u2 " File list from: 'wx list -w' ... \c"
  2371 			$WX list -w > $FLIST
  2372 			$WX comments > /tmp/$$.wx_comments
  2373 			wxfile=/tmp/$$.wx_comments
  2374 			print -u2 "done"
  2375 			flist_done=1
  2376 		fi
  2377 	fi
  2380 	# If by hook or by crook we've gotten a file list by now (perhaps
  2381 	# from the command line), eval it to extract environment variables from
  2382 	# it: This is step (3).
  2384 	env_from_flist
  2387 	# Continuing step (3): If we still have no file list, we'll try to get
  2388 	# it from teamware.
  2390 	if [[ -z $flist_done ]]; then
  2391 		flist_from_teamware
  2392 		env_from_flist
  2393 	fi
  2395 	if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
  2396 	    codemgr_ws=$PWD
  2397 	fi
  2399 	# Observe true directory name of CODEMGR_WS, as used later in
  2400 	# webrev title.
  2402 	if [[ -n $codemgr_ws ]]; then
  2403 	    codemgr_ws=$(cd $codemgr_ws;print $PWD)
  2404 	fi
  2406 	if [[ -n $codemgr_parent ]]; then
  2407 	    codemgr_parent=$(cd $codemgr_parent;print $PWD)
  2408 	fi
  2411 	# (4) If we still don't have a value for codemgr_parent, get it
  2412 	# from workspace.
  2414 	[[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
  2415 	[[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
  2417 	if [[ ! -d $codemgr_parent ]]; then
  2418 	    print -u2 "$CODEMGR_PARENT: no such parent workspace"
  2419 	    exit 1
  2420 	fi
  2423 	# Reset CODEMGR_WS to make sure teamware commands are happy.
  2425 	CODEMGR_WS=$codemgr_ws
  2426 	CWS=$codemgr_ws
  2427 	PWS=$codemgr_parent
  2428 elif [[ $SCM_MODE == "mercurial" ]]; then
  2429     if [[ -z $flist_done ]]; then
  2430 	flist_from_mercurial $PWS
  2431     fi
  2432 fi
  2435 # If the user didn't specify a -i option, check to see if there is a
  2436 # webrev-info file in the workspace directory.
  2438 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
  2439 	iflag=1
  2440 	INCLUDE_FILE="$CWS/webrev-info"
  2441 fi
  2443 if [[ -n $iflag ]]; then
  2444 	if [[ ! -r $INCLUDE_FILE ]]; then
  2445 		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
  2446 		    "not readable."
  2447 		exit 1
  2448 	else
  2450 		# $INCLUDE_FILE may be a relative path, and the script alters
  2451 		# PWD, so we just stash a copy in /tmp.
  2453 		cp $INCLUDE_FILE /tmp/$$.include
  2454 	fi
  2455 fi
  2458 # Output directory.
  2460 if [[ -z $WDIR ]]; then
  2461     WDIR=$CWS/webrev
  2462 else
  2463     # If the output directory doesn't end with '/webrev' or '/webrev/'
  2464     # then add '/webrev'. This is for backward compatibility
  2465     if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
  2466     then
  2467 	WDIR=$WDIR/webrev
  2468     fi
  2469 fi
  2470 # WDIR=${WDIR:-$CWS/webrev}
  2473 # Name of the webrev, derived from the workspace name; in the
  2474 # future this could potentially be an option.
  2476 # Let's keep what's after the last '/'
  2477 WNAME=${CWS##*/}
  2480 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
  2482 if [ ${WDIR%%/*} ]; then
  2483     if [[ -n $ISWIN ]]; then
  2484         if [ ${WDIR%%[A-Za-z]:*} ]; then
  2485 	    WDIR=$PWD/$WDIR
  2486         fi
  2487     else
  2488 	WDIR=$PWD/$WDIR
  2489     fi
  2490 fi
  2492 if [[ ! -d $WDIR ]]; then
  2493 	mkdir -p $WDIR
  2494 	[[ $? != 0 ]] && exit 1
  2495 fi
  2498 # Summarize what we're going to do.
  2500 print "      Workspace: $CWS"
  2501 if [[ -n $parent_webrev ]]; then
  2502     print "Compare against: webrev at $parent_webrev"
  2503 elif [[ -n $OUTPWS2 ]]; then
  2504     print "Compare against: $OUTPWS2"
  2505 fi
  2506 if [[ -n $HG_BRANCH ]]; then
  2507     print "         Branch: $HG_BRANCH"
  2508 fi
  2509 if [[ -n $rflag ]]; then
  2510         print "Compare against version: $PARENT_REV"
  2511 fi
  2512 [[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
  2513 print "      Output to: $WDIR"
  2516 # Save the file list in the webrev dir
  2518 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
  2521 #    Bug IDs will be replaced by a URL.  Order of precedence
  2522 #    is: default location, $WEBREV_BUGURL, the -O flag.
  2524 BUGURL='https://jbs.oracle.com/bugs/browse/'
  2525 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  2526 if [[ -n "$Oflag" ]]; then
  2527     CRID=`echo $CRID | sed -e 's/JDK-//'`
  2528     BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
  2529     IDPREFIX=''
  2530 else
  2531     IDPREFIX='JDK-'
  2532 fi
  2536 #    Likewise, ARC cases will be replaced by a URL.  Order of precedence
  2537 #    is: default, $WEBREV_SACURL, the -O flag.
  2539 #    Note that -O also triggers different substitution behavior for
  2540 #    SACURL.  See sac2url().
  2542 SACURL='http://sac.eng.sun.com'
  2543 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
  2544 [[ -n $Oflag ]] && \
  2545     SACURL='http://www.opensolaris.org/os/community/arc/caselog'
  2547 rm -f $WDIR/$WNAME.patch
  2548 rm -f $WDIR/$WNAME.ps
  2549 rm -f $WDIR/$WNAME.pdf
  2551 touch $WDIR/$WNAME.patch
  2553 print "   Output Files:"
  2556 # Clean up the file list: Remove comments, blank lines and env variables.
  2558 sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
  2559 FLIST=/tmp/$$.flist.clean
  2562 # Clean up residual raw files
  2564 if [ -d $WDIR/raw_files ]; then
  2565     rm -rf $WDIR/raw_files 2>/dev/null
  2566 fi
  2569 # Should we ignore changes in white spaces when generating diffs?
  2571 if [[ -n $bflag ]]; then
  2572     DIFFOPTS="-t"
  2573 else
  2574     DIFFOPTS="-bt"
  2575 fi
  2577 # First pass through the files: generate the per-file webrev HTML-files.
  2579 while read LINE
  2580 do
  2581 	set - $LINE
  2582 	P=$1
  2584         if [[ $1 == "Revision:" ]]; then
  2585             OUTREV=$2
  2586             continue
  2587         fi
  2589 	# Normally, each line in the file list is just a pathname of a
  2590 	# file that has been modified or created in the child.  A file
  2591 	# that is renamed in the child workspace has two names on the
  2592 	# line: new name followed by the old name.
  2594 	oldname=""
  2595 	oldpath=""
  2596 	rename=
  2597 	if [[ $# -eq 2 ]]; then
  2598 		PP=$2			# old filename
  2599 		oldname=" (was $PP)"
  2600 		oldpath="$PP"
  2601 		rename=1
  2602         	PDIR=${PP%/*}
  2603         	if [[ $PDIR == $PP ]]; then
  2604 			PDIR="."   # File at root of workspace
  2605 		fi
  2607 		PF=${PP##*/}
  2609 	        DIR=${P%/*}
  2610 	        if [[ $DIR == $P ]]; then
  2611 			DIR="."   # File at root of workspace
  2612 		fi
  2614 		F=${P##*/}
  2615         else
  2616 	        DIR=${P%/*}
  2617 	        if [[ "$DIR" == "$P" ]]; then
  2618 			DIR="."   # File at root of workspace
  2619 		fi
  2621 		F=${P##*/}
  2623 		PP=$P
  2624 		PDIR=$DIR
  2625 		PF=$F
  2626 	fi
  2628         # Make the webrev directory if necessary as it may have been
  2629         # removed because it was empty
  2630         if [ ! -d $CWS/$DIR ]; then
  2631 	    mkdir -p $CWS/$DIR
  2632         fi
  2634 	COMM=`getcomments html $P $PP`
  2636 	print "\t$P$oldname\n\t\t\c"
  2638 	# Make the webrev mirror directory if necessary
  2639 	mkdir -p $WDIR/$DIR
  2641 	# cd to the directory so the names are short
  2642 	cd $CWS/$DIR
  2645 	# If we're in OpenSolaris mode, we enforce a minor policy:
  2646 	# help to make sure the reviewer doesn't accidentally publish
  2647 	# source which is in usr/closed/*
  2649 	if [[ -n $Oflag ]]; then
  2650 		pclosed=${P##usr/closed/}
  2651 		if [[ $pclosed != $P ]]; then
  2652 			print "*** Omitting closed source for OpenSolaris" \
  2653 			    "mode review"
  2654 			continue
  2655 		fi
  2656 	fi
  2659 	# We stash old and new files into parallel directories in /tmp
  2660 	# and do our diffs there.  This makes it possible to generate
  2661 	# clean looking diffs which don't have absolute paths present.
  2663 	olddir=$WDIR/raw_files/old
  2664 	newdir=$WDIR/raw_files/new
  2665 	mkdir -p $olddir
  2666 	mkdir -p $newdir
  2667 	mkdir -p $olddir/$PDIR
  2668 	mkdir -p $newdir/$DIR
  2670 	build_old_new $olddir $newdir $DIR $F
  2672 	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
  2673 		print "*** Error: file not in parent or child"
  2674 		continue
  2675 	fi
  2677 	cd $WDIR/raw_files
  2678 	ofile=old/$PDIR/$PF
  2679 	nfile=new/$DIR/$F
  2681 	mv_but_nodiff=
  2682 	cmp $ofile $nfile > /dev/null 2>&1
  2683 	if [[ $? == 0 && $rename == 1 ]]; then
  2684 		mv_but_nodiff=1
  2685 	fi
  2688         # Cleaning up
  2690         rm -f $WDIR/$DIR/$F.cdiff.html
  2691         rm -f $WDIR/$DIR/$F.udiff.html
  2692         rm -f $WDIR/$DIR/$F.wdiff.html
  2693         rm -f $WDIR/$DIR/$F.sdiff.html
  2694         rm -f $WDIR/$DIR/$F-.html
  2695         rm -f $WDIR/$DIR/$F.html
  2697 	its_a_jar=
  2698 	if expr $F : '.*\.jar' >/dev/null; then
  2699 	    its_a_jar=1
  2700 	    # It's a JAR file, let's do it differntly
  2701 	    if [[ -z $JAR ]]; then
  2702 		print "No access to jar, so can't produce diffs for jar files"
  2703 	    else
  2704 		if [ -f $ofile ]; then
  2705 		    $JAR -tvf $ofile >"$ofile".lst
  2706 		fi
  2707 		if [ -f $nfile ]; then
  2708 		    $JAR -tvf $nfile >"$nfile".lst
  2709 		fi
  2711 		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2713 		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
  2714 		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2715 			> $WDIR/$DIR/$F.cdiff.html
  2716 		    print " cdiffs\c"
  2718 		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
  2719 		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2720 			> $WDIR/$DIR/$F.udiff.html
  2722 		    print " udiffs\c"
  2724 		    if [[ -x $WDIFF ]]; then
  2725 			$WDIFF -c "$COMM" \
  2726 			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
  2727 			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2728 			if [[ $? -eq 0 ]]; then
  2729 			    print " wdiffs\c"
  2730 			else
  2731 			    print " wdiffs[fail]\c"
  2732 			fi
  2733 		    fi
  2735 		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2736 			> $WDIR/$DIR/$F.sdiff.html
  2737 		    print " sdiffs\c"
  2739 		    print " frames\c"
  2741 		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2743 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2745 		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2746 		# renamed file: may also have differences
  2747 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2748 		elif [[ -f $nfile ]]; then
  2749 		# new file: count added lines
  2750 		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
  2751 		elif [[ -f $ofile ]]; then
  2752 		# old file: count deleted lines
  2753 		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
  2754 		fi
  2755 	    fi
  2756 	else
  2759 	    # If we have old and new versions of the file then run the
  2760 	    # appropriate diffs.  This is complicated by a couple of factors:
  2762 	    #	- renames must be handled specially: we emit a 'remove'
  2763 	    #	  diff and an 'add' diff
  2764 	    #	- new files and deleted files must be handled specially
  2765 	    #	- Solaris patch(1m) can't cope with file creation
  2766 	    #	  (and hence renames) as of this writing.
  2767 	    #   - To make matters worse, gnu patch doesn't interpret the
  2768 	    #	  output of Solaris diff properly when it comes to
  2769 	    #	  adds and deletes.  We need to do some "cleansing"
  2770 	    #     transformations:
  2771 	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
  2772 	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
  2774 	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
  2775 	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
  2777 	    rm -f $WDIR/$DIR/$F.patch
  2778 	    if [[ -z $rename ]]; then
  2779 		if [ ! -f $ofile ]; then
  2780 		    diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2781 			> $WDIR/$DIR/$F.patch
  2782 		elif [ ! -f $nfile ]; then
  2783 		    diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2784 			> $WDIR/$DIR/$F.patch
  2785 		else
  2786 		    diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  2787 		fi
  2788 	    else
  2789 		diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2790 		    > $WDIR/$DIR/$F.patch
  2792 		diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2793 		    >> $WDIR/$DIR/$F.patch
  2795 	    fi
  2799 	# Tack the patch we just made onto the accumulated patch for the
  2800 	# whole wad.
  2802 	    cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  2804 	    print " patch\c"
  2806 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2808 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  2809 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2810 		    > $WDIR/$DIR/$F.cdiff.html
  2811 		print " cdiffs\c"
  2813 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  2814 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2815 		    > $WDIR/$DIR/$F.udiff.html
  2817 		print " udiffs\c"
  2819 		if [[ -x $WDIFF ]]; then
  2820 		    $WDIFF -c "$COMM" \
  2821 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  2822 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2823 		    if [[ $? -eq 0 ]]; then
  2824 			print " wdiffs\c"
  2825 		    else
  2826 			print " wdiffs[fail]\c"
  2827 		    fi
  2828 		fi
  2830 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2831 		    > $WDIR/$DIR/$F.sdiff.html
  2832 		print " sdiffs\c"
  2834 		print " frames\c"
  2836 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2838 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2840 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2841 		# renamed file: may also have differences
  2842 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2843 	    elif [[ -f $nfile ]]; then
  2844 		# new file: count added lines
  2845 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  2846 	    elif [[ -f $ofile ]]; then
  2847 		# old file: count deleted lines
  2848 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  2849 	    fi
  2850 	fi
  2852 	# Now we generate the postscript for this file.  We generate diffs
  2853 	# only in the event that there is delta, or the file is new (it seems
  2854 	# tree-killing to print out the contents of deleted files).
  2856 	if [[ -f $nfile ]]; then
  2857 		ocr=$ofile
  2858 		[[ ! -f $ofile ]] && ocr=/dev/null
  2860 		if [[ -z $mv_but_nodiff ]]; then
  2861 			textcomm=`getcomments text $P $PP`
  2862 			if [[ -x $CODEREVIEW ]]; then
  2863 				$CODEREVIEW -y "$textcomm" \
  2864 				    -e $ocr $nfile \
  2865 				    > /tmp/$$.psfile 2>/dev/null &&
  2866 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  2867 				if [[ $? -eq 0 ]]; then
  2868 					print " ps\c"
  2869 				else
  2870 					print " ps[fail]\c"
  2871 				fi
  2872 			fi
  2873 		fi
  2874 	fi
  2876 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  2877 	    if [[ -n $its_a_jar ]]; then
  2878 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  2879 	    else
  2880 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  2881 	    fi
  2882 		print " old\c"
  2883 	fi
  2885 	if [[ -f $nfile ]]; then
  2886 	    if [[ -n $its_a_jar ]]; then
  2887 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  2888 	    else
  2889 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  2890 	    fi
  2891 		print " new\c"
  2892 	fi
  2894 	print
  2895 done < $FLIST
  2897 frame_nav_js > $WDIR/ancnav.js
  2898 frame_navigation > $WDIR/ancnav.html
  2900 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  2901 	print " Generating PDF: \c"
  2902 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  2903 	print "Done."
  2904 fi
  2906 # Now build the index.html file that contains
  2907 # links to the source files and their diffs.
  2909 cd $CWS
  2911 # Save total changed lines for Code Inspection.
  2912 print "$TOTL" > $WDIR/TotalChangedLines
  2914 print "     index.html: \c"
  2915 INDEXFILE=$WDIR/index.html
  2916 exec 3<&1			# duplicate stdout to FD3.
  2917 exec 1<&-			# Close stdout.
  2918 exec > $INDEXFILE		# Open stdout to index file.
  2920 print "$HTML<head>"
  2921 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  2922 print "$STDHEAD"
  2923 print "<title>$WNAME</title>"
  2924 print "</head>"
  2925 print "<body id=\"SUNWwebrev\">"
  2926 print "<div class=\"summary\">"
  2927 print "<h2>Code Review for $WNAME</h2>"
  2929 print "<table>"
  2931 if [[ -z $uflag ]]
  2932 then
  2933     if [[ $SCM_MODE == "mercurial" ]]
  2934     then
  2936         # Let's try to extract the user name from the .hgrc file
  2938 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  2939     fi
  2941     if [[ -z $username ]]
  2942     then
  2944         # Figure out the username and gcos name.  To maintain compatibility
  2945         # with passwd(4), we must support '&' substitutions.
  2947 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  2948 	if [[ -x $GETENT ]]; then
  2949 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  2950 	fi
  2951 	userupper=`print "$username" | sed 's/\<./\u&/g'`
  2952 	realname=`print $realname | sed s/\&/$userupper/`
  2953     fi
  2954 fi
  2956 date="on `date`"
  2958 if [[ -n "$username" && -n "$realname" ]]; then
  2959 	print "<tr><th>Prepared by:</th>"
  2960 	print "<td>$realname ($username) $date</td></tr>"
  2961 elif [[ -n "$username" ]]; then
  2962 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  2963 fi
  2965 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  2966 if [[ -n $parent_webrev ]]; then
  2967         print "<tr><th>Compare against:</th><td>"
  2968 	print "webrev at $parent_webrev"
  2969 else
  2970     if [[ -n $OUTPWS2 ]]; then
  2971         print "<tr><th>Compare against:</th><td>"
  2972 	print "$OUTPWS2"
  2973     fi
  2974 fi
  2975 print "</td></tr>"
  2976 if [[ -n $rflag ]]; then
  2977     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  2978 elif [[ -n $OUTREV ]]; then
  2979     if [[ -z $forestflag ]]; then
  2980         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  2981     fi
  2982 fi
  2983 if [[ -n $HG_BRANCH ]]; then
  2984     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  2985 fi
  2987 print "<tr><th>Summary of changes:</th><td>"
  2988 printCI $TOTL $TINS $TDEL $TMOD $TUNC
  2989 print "</td></tr>"
  2991 if [[ -f $WDIR/$WNAME.patch ]]; then
  2992 	print "<tr><th>Patch of changes:</th><td>"
  2993 	print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  2994 fi
  2995 if [[ -f $WDIR/$WNAME.pdf ]]; then
  2996 	print "<tr><th>Printable review:</th><td>"
  2997 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  2998 fi
  3000 if [[ -n "$iflag" ]]; then
  3001 	print "<tr><th>Author comments:</th><td><div>"
  3002 	cat /tmp/$$.include
  3003 	print "</div></td></tr>"
  3004 fi
  3005 # Add links to referenced CRs, if any
  3006 # external URL has a <title> like:
  3007 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
  3008 # while internal URL has <title> like:
  3009 # <title>[#JDK-6641309] Wrong Cookie separator used in HttpURLConnection</title>
  3011 if [[ -n $CRID ]]; then
  3012     for id in $CRID
  3013     do
  3014         if [[ -z "$Oflag" ]]; then
  3015             #add "JDK-" to raw bug id for jbs links.
  3016             id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
  3017         fi
  3018         print "<tr><th>Bug id:</th><td>"
  3019         url="${BUGURL}${id}"
  3020         if [[ -n "$Oflag" ]]; then
  3021             cleanup='s/Bug ID: \([0-9]\{5,\}\) \(.*\)/JDK-\1 : \2/'
  3022         else
  3023             cleanup='s|\[#\(JDK-[0-9]\{5,\}\)\] \(.*\)|\1 : \2|'
  3024         fi
  3025         if [[ -n $WGET ]]; then
  3026             msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed "$cleanup" | html_quote`
  3027         fi
  3028         if [[ -z $msg ]]; then
  3029             msg="${id}"
  3030         fi
  3032         print "<a href=\"$url\">$msg</a>"
  3034         print "</td></tr>"
  3035     done
  3036 fi
  3037 print "<tr><th>Legend:</th><td>"
  3038 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>"
  3039 print "</table>"
  3040 print "</div>"
  3043 # Second pass through the files: generate the rest of the index file
  3045 while read LINE
  3046 do
  3047 	set - $LINE
  3048         if [[ $1 == "Revision:" ]]; then
  3049             FIRST_CREV=`expr $3 + 1`
  3050             continue
  3051         fi
  3052 	P=$1
  3054 	if [[ $# == 2 ]]; then
  3055 		PP=$2
  3056 		oldname=" <i>(was $PP)</i>"
  3058 	else
  3059 		PP=$P
  3060 		oldname=""
  3061 	fi
  3063 	DIR=${P%/*}
  3064 	if [[ $DIR == $P ]]; then
  3065 		DIR="."   # File at root of workspace
  3066 	fi
  3068 	# Avoid processing the same file twice.
  3069 	# It's possible for renamed files to
  3070 	# appear twice in the file list
  3072 	F=$WDIR/$P
  3074 	print "<p><code>"
  3076 	# If there's a diffs file, make diffs links
  3078         NODIFFS=
  3079 	if [[ -f $F.cdiff.html ]]; then
  3080 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  3081 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  3083 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  3084 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  3085 		fi
  3087 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  3089 		print "<a href=\"$P.frames.html\">Frames</a>"
  3090 	else
  3091                 NODIFFS=1
  3092 		print " ------ ------ ------"
  3094 		if [[ -x $WDIFF ]]; then
  3095 			print " ------"
  3096 		fi
  3098 		print " ------"
  3099 	fi
  3101 	# If there's an old file, make the link
  3103         NOOLD=
  3104 	if [[ -f $F-.html ]]; then
  3105 		print "<a href=\"$P-.html\">Old</a>"
  3106 	else
  3107                 NOOLD=1
  3108 		print " ---"
  3109 	fi
  3111 	# If there's an new file, make the link
  3113         NONEW=
  3114 	if [[ -f $F.html ]]; then
  3115 		print "<a href=\"$P.html\">New</a>"
  3116 	else
  3117                 NONEW=1
  3118 		print " ---"
  3119 	fi
  3121 	if [[ -f $F.patch ]]; then
  3122 		print "<a href=\"$P.patch\">Patch</a>"
  3123 	else
  3124 		print " -----"
  3125 	fi
  3127 	if [[ -f $WDIR/raw_files/new/$P ]]; then
  3128 		print "<a href=\"raw_files/new/$P\">Raw</a>"
  3129 	else
  3130 		print " ---"
  3131 	fi
  3132         print "</code>"
  3133         if [[ -n $NODIFFS && -z $oldname ]]; then
  3134             if [[ -n $NOOLD ]]; then
  3135                 print "<font color=green><b>$P</b></font>"
  3136             elif [[ -n $NONEW ]]; then
  3137                 print "<font color=red><b>$P</b></font>"
  3138             fi
  3139         else
  3140 	    print "<b>$P</b> $oldname"
  3141         fi
  3144 	# Check for usr/closed
  3146 	if [ ! -z "$Oflag" ]; then
  3147 		if [[ $P == usr/closed/* ]]; then
  3148 			print "&nbsp;&nbsp;<i>Closed source: omitted from" \
  3149 			    "this review</i>"
  3150 		fi
  3151 	fi
  3153 	print "</p><blockquote>\c"
  3154 	# Insert delta comments if any
  3155 	comments=`getcomments html $P $PP`
  3156 	if [ -n "$comments" ]; then
  3157 	    print "<pre>$comments</pre>"
  3158 	fi
  3160 	# Add additional comments comment
  3162 	print "<!-- Add comments to explain changes in $P here -->"
  3164 	# Add count of changes.
  3166 	if [[ -f $F.count ]]; then
  3167 	    cat $F.count
  3168 	    rm $F.count
  3169 	fi
  3170         print "</blockquote>"
  3171 done < $FLIST
  3173 print
  3174 print
  3175 print "<hr />"
  3176 print "<p style=\"font-size: small\">"
  3177 print "This code review page was prepared using <b>$0</b>"
  3178 print "(vers $WEBREV_UPDATED)."
  3179 print "</body>"
  3180 print "</html>"
  3182 if [[ -n $ZIP ]]; then
  3183     # Let's generate a zip file for convenience
  3184     cd $WDIR/..
  3185     if [ -f webrev.zip ]; then
  3186 	rm webrev.zip
  3187     fi
  3188     $ZIP -r webrev webrev >/dev/null 2>&1
  3189 fi
  3191 exec 1<&-			# Close FD 1.
  3192 exec 1<&3			# dup FD 3 to restore stdout.
  3193 exec 3<&-			# close FD 3.
  3195 print "Done."
  3196 print "Output to: $WDIR"

mercurial