make/scripts/webrev.ksh

Tue, 01 Mar 2011 11:54:05 -0800

author
ohair
date
Tue, 01 Mar 2011 11:54:05 -0800
changeset 314
47ad81d343e8
child 616
168dd033604a
permissions
-rw-r--r--

7023111: Add webrev script to make/scripts
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, 2011, 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
    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'&\">&</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="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
   234 #          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='http://monaco.sfbay.sun.com/detail.jsp?cr='
  2525 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  2526 [[ -n "$Oflag" ]] && \
  2527     BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
  2530 #    Likewise, ARC cases will be replaced by a URL.  Order of precedence
  2531 #    is: default, $WEBREV_SACURL, the -O flag.
  2533 #    Note that -O also triggers different substitution behavior for
  2534 #    SACURL.  See sac2url().
  2536 SACURL='http://sac.eng.sun.com'
  2537 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
  2538 [[ -n $Oflag ]] && \
  2539     SACURL='http://www.opensolaris.org/os/community/arc/caselog'
  2541 rm -f $WDIR/$WNAME.patch
  2542 rm -f $WDIR/$WNAME.ps
  2543 rm -f $WDIR/$WNAME.pdf
  2545 touch $WDIR/$WNAME.patch
  2547 print "   Output Files:"
  2550 # Clean up the file list: Remove comments, blank lines and env variables.
  2552 sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
  2553 FLIST=/tmp/$$.flist.clean
  2556 # Clean up residual raw files
  2558 if [ -d $WDIR/raw_files ]; then
  2559     rm -rf $WDIR/raw_files 2>/dev/null
  2560 fi
  2563 # Should we ignore changes in white spaces when generating diffs?
  2565 if [[ -n $bflag ]]; then
  2566     DIFFOPTS="-t"
  2567 else
  2568     DIFFOPTS="-bt"
  2569 fi
  2571 # First pass through the files: generate the per-file webrev HTML-files.
  2573 while read LINE
  2574 do
  2575 	set - $LINE
  2576 	P=$1
  2578         if [[ $1 == "Revision:" ]]; then
  2579             OUTREV=$2
  2580             continue
  2581         fi
  2583 	# Normally, each line in the file list is just a pathname of a
  2584 	# file that has been modified or created in the child.  A file
  2585 	# that is renamed in the child workspace has two names on the
  2586 	# line: new name followed by the old name.
  2588 	oldname=""
  2589 	oldpath=""
  2590 	rename=
  2591 	if [[ $# -eq 2 ]]; then
  2592 		PP=$2			# old filename
  2593 		oldname=" (was $PP)"
  2594 		oldpath="$PP"
  2595 		rename=1
  2596         	PDIR=${PP%/*}
  2597         	if [[ $PDIR == $PP ]]; then
  2598 			PDIR="."   # File at root of workspace
  2599 		fi
  2601 		PF=${PP##*/}
  2603 	        DIR=${P%/*}
  2604 	        if [[ $DIR == $P ]]; then
  2605 			DIR="."   # File at root of workspace
  2606 		fi
  2608 		F=${P##*/}
  2609         else
  2610 	        DIR=${P%/*}
  2611 	        if [[ "$DIR" == "$P" ]]; then
  2612 			DIR="."   # File at root of workspace
  2613 		fi
  2615 		F=${P##*/}
  2617 		PP=$P
  2618 		PDIR=$DIR
  2619 		PF=$F
  2620 	fi
  2622         # Make the webrev directory if necessary as it may have been
  2623         # removed because it was empty
  2624         if [ ! -d $CWS/$DIR ]; then
  2625 	    mkdir -p $CWS/$DIR
  2626         fi
  2628 	COMM=`getcomments html $P $PP`
  2630 	print "\t$P$oldname\n\t\t\c"
  2632 	# Make the webrev mirror directory if necessary
  2633 	mkdir -p $WDIR/$DIR
  2635 	# cd to the directory so the names are short
  2636 	cd $CWS/$DIR
  2639 	# If we're in OpenSolaris mode, we enforce a minor policy:
  2640 	# help to make sure the reviewer doesn't accidentally publish
  2641 	# source which is in usr/closed/*
  2643 	if [[ -n $Oflag ]]; then
  2644 		pclosed=${P##usr/closed/}
  2645 		if [[ $pclosed != $P ]]; then
  2646 			print "*** Omitting closed source for OpenSolaris" \
  2647 			    "mode review"
  2648 			continue
  2649 		fi
  2650 	fi
  2653 	# We stash old and new files into parallel directories in /tmp
  2654 	# and do our diffs there.  This makes it possible to generate
  2655 	# clean looking diffs which don't have absolute paths present.
  2657 	olddir=$WDIR/raw_files/old
  2658 	newdir=$WDIR/raw_files/new
  2659 	mkdir -p $olddir
  2660 	mkdir -p $newdir
  2661 	mkdir -p $olddir/$PDIR
  2662 	mkdir -p $newdir/$DIR
  2664 	build_old_new $olddir $newdir $DIR $F
  2666 	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
  2667 		print "*** Error: file not in parent or child"
  2668 		continue
  2669 	fi
  2671 	cd $WDIR/raw_files
  2672 	ofile=old/$PDIR/$PF
  2673 	nfile=new/$DIR/$F
  2675 	mv_but_nodiff=
  2676 	cmp $ofile $nfile > /dev/null 2>&1
  2677 	if [[ $? == 0 && $rename == 1 ]]; then
  2678 		mv_but_nodiff=1
  2679 	fi
  2682         # Cleaning up
  2684         rm -f $WDIR/$DIR/$F.cdiff.html
  2685         rm -f $WDIR/$DIR/$F.udiff.html
  2686         rm -f $WDIR/$DIR/$F.wdiff.html
  2687         rm -f $WDIR/$DIR/$F.sdiff.html
  2688         rm -f $WDIR/$DIR/$F-.html
  2689         rm -f $WDIR/$DIR/$F.html
  2691 	its_a_jar=
  2692 	if expr $F : '.*\.jar' >/dev/null; then
  2693 	    its_a_jar=1
  2694 	    # It's a JAR file, let's do it differntly
  2695 	    if [[ -z $JAR ]]; then
  2696 		print "No access to jar, so can't produce diffs for jar files"
  2697 	    else
  2698 		if [ -f $ofile ]; then
  2699 		    $JAR -tvf $ofile >"$ofile".lst
  2700 		fi
  2701 		if [ -f $nfile ]; then
  2702 		    $JAR -tvf $nfile >"$nfile".lst
  2703 		fi
  2705 		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2707 		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
  2708 		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2709 			> $WDIR/$DIR/$F.cdiff.html
  2710 		    print " cdiffs\c"
  2712 		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
  2713 		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2714 			> $WDIR/$DIR/$F.udiff.html
  2716 		    print " udiffs\c"
  2718 		    if [[ -x $WDIFF ]]; then
  2719 			$WDIFF -c "$COMM" \
  2720 			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
  2721 			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2722 			if [[ $? -eq 0 ]]; then
  2723 			    print " wdiffs\c"
  2724 			else
  2725 			    print " wdiffs[fail]\c"
  2726 			fi
  2727 		    fi
  2729 		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2730 			> $WDIR/$DIR/$F.sdiff.html
  2731 		    print " sdiffs\c"
  2733 		    print " frames\c"
  2735 		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2737 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2739 		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2740 		# renamed file: may also have differences
  2741 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2742 		elif [[ -f $nfile ]]; then
  2743 		# new file: count added lines
  2744 		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
  2745 		elif [[ -f $ofile ]]; then
  2746 		# old file: count deleted lines
  2747 		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
  2748 		fi
  2749 	    fi
  2750 	else
  2753 	    # If we have old and new versions of the file then run the
  2754 	    # appropriate diffs.  This is complicated by a couple of factors:
  2756 	    #	- renames must be handled specially: we emit a 'remove'
  2757 	    #	  diff and an 'add' diff
  2758 	    #	- new files and deleted files must be handled specially
  2759 	    #	- Solaris patch(1m) can't cope with file creation
  2760 	    #	  (and hence renames) as of this writing.
  2761 	    #   - To make matters worse, gnu patch doesn't interpret the
  2762 	    #	  output of Solaris diff properly when it comes to
  2763 	    #	  adds and deletes.  We need to do some "cleansing"
  2764 	    #     transformations:
  2765 	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
  2766 	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
  2768 	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
  2769 	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
  2771 	    rm -f $WDIR/$DIR/$F.patch
  2772 	    if [[ -z $rename ]]; then
  2773 		if [ ! -f $ofile ]; then
  2774 		    diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2775 			> $WDIR/$DIR/$F.patch
  2776 		elif [ ! -f $nfile ]; then
  2777 		    diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2778 			> $WDIR/$DIR/$F.patch
  2779 		else
  2780 		    diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  2781 		fi
  2782 	    else
  2783 		diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2784 		    > $WDIR/$DIR/$F.patch
  2786 		diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2787 		    >> $WDIR/$DIR/$F.patch
  2789 	    fi
  2793 	# Tack the patch we just made onto the accumulated patch for the
  2794 	# whole wad.
  2796 	    cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  2798 	    print " patch\c"
  2800 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2802 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  2803 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2804 		    > $WDIR/$DIR/$F.cdiff.html
  2805 		print " cdiffs\c"
  2807 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  2808 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2809 		    > $WDIR/$DIR/$F.udiff.html
  2811 		print " udiffs\c"
  2813 		if [[ -x $WDIFF ]]; then
  2814 		    $WDIFF -c "$COMM" \
  2815 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  2816 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2817 		    if [[ $? -eq 0 ]]; then
  2818 			print " wdiffs\c"
  2819 		    else
  2820 			print " wdiffs[fail]\c"
  2821 		    fi
  2822 		fi
  2824 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2825 		    > $WDIR/$DIR/$F.sdiff.html
  2826 		print " sdiffs\c"
  2828 		print " frames\c"
  2830 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2832 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2834 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2835 		# renamed file: may also have differences
  2836 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2837 	    elif [[ -f $nfile ]]; then
  2838 		# new file: count added lines
  2839 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  2840 	    elif [[ -f $ofile ]]; then
  2841 		# old file: count deleted lines
  2842 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  2843 	    fi
  2844 	fi
  2846 	# Now we generate the postscript for this file.  We generate diffs
  2847 	# only in the event that there is delta, or the file is new (it seems
  2848 	# tree-killing to print out the contents of deleted files).
  2850 	if [[ -f $nfile ]]; then
  2851 		ocr=$ofile
  2852 		[[ ! -f $ofile ]] && ocr=/dev/null
  2854 		if [[ -z $mv_but_nodiff ]]; then
  2855 			textcomm=`getcomments text $P $PP`
  2856 			if [[ -x $CODEREVIEW ]]; then
  2857 				$CODEREVIEW -y "$textcomm" \
  2858 				    -e $ocr $nfile \
  2859 				    > /tmp/$$.psfile 2>/dev/null &&
  2860 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  2861 				if [[ $? -eq 0 ]]; then
  2862 					print " ps\c"
  2863 				else
  2864 					print " ps[fail]\c"
  2865 				fi
  2866 			fi
  2867 		fi
  2868 	fi
  2870 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  2871 	    if [[ -n $its_a_jar ]]; then
  2872 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  2873 	    else
  2874 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  2875 	    fi
  2876 		print " old\c"
  2877 	fi
  2879 	if [[ -f $nfile ]]; then
  2880 	    if [[ -n $its_a_jar ]]; then
  2881 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  2882 	    else
  2883 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  2884 	    fi
  2885 		print " new\c"
  2886 	fi
  2888 	print
  2889 done < $FLIST
  2891 frame_nav_js > $WDIR/ancnav.js
  2892 frame_navigation > $WDIR/ancnav.html
  2894 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  2895 	print " Generating PDF: \c"
  2896 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  2897 	print "Done."
  2898 fi
  2900 # Now build the index.html file that contains
  2901 # links to the source files and their diffs.
  2903 cd $CWS
  2905 # Save total changed lines for Code Inspection.
  2906 print "$TOTL" > $WDIR/TotalChangedLines
  2908 print "     index.html: \c"
  2909 INDEXFILE=$WDIR/index.html
  2910 exec 3<&1			# duplicate stdout to FD3.
  2911 exec 1<&-			# Close stdout.
  2912 exec > $INDEXFILE		# Open stdout to index file.
  2914 print "$HTML<head>"
  2915 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  2916 print "$STDHEAD"
  2917 print "<title>$WNAME</title>"
  2918 print "</head>"
  2919 print "<body id=\"SUNWwebrev\">"
  2920 print "<div class=\"summary\">"
  2921 print "<h2>Code Review for $WNAME</h2>"
  2923 print "<table>"
  2925 if [[ -z $uflag ]]
  2926 then
  2927     if [[ $SCM_MODE == "mercurial" ]]
  2928     then
  2930         # Let's try to extract the user name from the .hgrc file
  2932 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  2933     fi
  2935     if [[ -z $username ]]
  2936     then
  2938         # Figure out the username and gcos name.  To maintain compatibility
  2939         # with passwd(4), we must support '&' substitutions.
  2941 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  2942 	if [[ -x $GETENT ]]; then
  2943 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  2944 	fi
  2945 	userupper=`print "$username" | sed 's/\<./\u&/g'`
  2946 	realname=`print $realname | sed s/\&/$userupper/`
  2947     fi
  2948 fi
  2950 date="on `date`"
  2952 if [[ -n "$username" && -n "$realname" ]]; then
  2953 	print "<tr><th>Prepared by:</th>"
  2954 	print "<td>$realname ($username) $date</td></tr>"
  2955 elif [[ -n "$username" ]]; then
  2956 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  2957 fi
  2959 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  2960 if [[ -n $parent_webrev ]]; then
  2961         print "<tr><th>Compare against:</th><td>"
  2962 	print "webrev at $parent_webrev"
  2963 else
  2964     if [[ -n $OUTPWS2 ]]; then
  2965         print "<tr><th>Compare against:</th><td>"
  2966 	print "$OUTPWS2"
  2967     fi
  2968 fi
  2969 print "</td></tr>"
  2970 if [[ -n $rflag ]]; then
  2971     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  2972 elif [[ -n $OUTREV ]]; then
  2973     if [[ -z $forestflag ]]; then
  2974         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  2975     fi
  2976 fi
  2977 if [[ -n $HG_BRANCH ]]; then
  2978     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  2979 fi
  2981 print "<tr><th>Summary of changes:</th><td>"
  2982 printCI $TOTL $TINS $TDEL $TMOD $TUNC
  2983 print "</td></tr>"
  2985 if [[ -f $WDIR/$WNAME.patch ]]; then
  2986 	print "<tr><th>Patch of changes:</th><td>"
  2987 	print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  2988 fi
  2989 if [[ -f $WDIR/$WNAME.pdf ]]; then
  2990 	print "<tr><th>Printable review:</th><td>"
  2991 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  2992 fi
  2994 if [[ -n "$iflag" ]]; then
  2995 	print "<tr><th>Author comments:</th><td><div>"
  2996 	cat /tmp/$$.include
  2997 	print "</div></td></tr>"
  2998 fi
  2999 # Add links to referenced CRs, if any
  3000 # external URL has a <title> like:
  3001 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
  3002 # while internal URL has <title> like:
  3003 # <title>6641309: Wrong Cookie separator used in HttpURLConnection</title>
  3005 if [[ -n $CRID ]]; then
  3006     for id in $CRID
  3007     do
  3008         print "<tr><th>Bug id:</th><td>"
  3009         url="${BUGURL}${id}"
  3010         if [[ -n $WGET ]]; then
  3011             msg=`$WGET -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed 's/Bug ID://'`
  3012         fi
  3013         if [[ -n $msg ]]; then
  3014             print "<a href=\"$url\">$msg</a>"
  3015         else
  3016             print $id | bug2url
  3017         fi
  3019         print "</td></tr>"
  3020     done
  3021 fi
  3022 print "<tr><th>Legend:</th><td>"
  3023 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>"
  3024 print "</table>"
  3025 print "</div>"
  3028 # Second pass through the files: generate the rest of the index file
  3030 while read LINE
  3031 do
  3032 	set - $LINE
  3033         if [[ $1 == "Revision:" ]]; then
  3034             FIRST_CREV=`expr $3 + 1`
  3035             continue
  3036         fi
  3037 	P=$1
  3039 	if [[ $# == 2 ]]; then
  3040 		PP=$2
  3041 		oldname=" <i>(was $PP)</i>"
  3043 	else
  3044 		PP=$P
  3045 		oldname=""
  3046 	fi
  3048 	DIR=${P%/*}
  3049 	if [[ $DIR == $P ]]; then
  3050 		DIR="."   # File at root of workspace
  3051 	fi
  3053 	# Avoid processing the same file twice.
  3054 	# It's possible for renamed files to
  3055 	# appear twice in the file list
  3057 	F=$WDIR/$P
  3059 	print "<p><code>"
  3061 	# If there's a diffs file, make diffs links
  3063         NODIFFS=
  3064 	if [[ -f $F.cdiff.html ]]; then
  3065 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  3066 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  3068 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  3069 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  3070 		fi
  3072 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  3074 		print "<a href=\"$P.frames.html\">Frames</a>"
  3075 	else
  3076                 NODIFFS=1
  3077 		print " ------ ------ ------"
  3079 		if [[ -x $WDIFF ]]; then
  3080 			print " ------"
  3081 		fi
  3083 		print " ------"
  3084 	fi
  3086 	# If there's an old file, make the link
  3088         NOOLD=
  3089 	if [[ -f $F-.html ]]; then
  3090 		print "<a href=\"$P-.html\">Old</a>"
  3091 	else
  3092                 NOOLD=1
  3093 		print " ---"
  3094 	fi
  3096 	# If there's an new file, make the link
  3098         NONEW=
  3099 	if [[ -f $F.html ]]; then
  3100 		print "<a href=\"$P.html\">New</a>"
  3101 	else
  3102                 NONEW=1
  3103 		print " ---"
  3104 	fi
  3106 	if [[ -f $F.patch ]]; then
  3107 		print "<a href=\"$P.patch\">Patch</a>"
  3108 	else
  3109 		print " -----"
  3110 	fi
  3112 	if [[ -f $WDIR/raw_files/new/$P ]]; then
  3113 		print "<a href=\"raw_files/new/$P\">Raw</a>"
  3114 	else
  3115 		print " ---"
  3116 	fi
  3117         print "</code>"
  3118         if [[ -n $NODIFFS && -z $oldname ]]; then
  3119             if [[ -n $NOOLD ]]; then
  3120                 print "<font color=green><b>$P</b></font>"
  3121             elif [[ -n $NONEW ]]; then
  3122                 print "<font color=red><b>$P</b></font>"
  3123             fi
  3124         else
  3125 	    print "<b>$P</b> $oldname"
  3126         fi
  3129 	# Check for usr/closed
  3131 	if [ ! -z "$Oflag" ]; then
  3132 		if [[ $P == usr/closed/* ]]; then
  3133 			print "&nbsp;&nbsp;<i>Closed source: omitted from" \
  3134 			    "this review</i>"
  3135 		fi
  3136 	fi
  3138 	print "</p><blockquote>\c"
  3139 	# Insert delta comments if any
  3140 	comments=`getcomments html $P $PP`
  3141 	if [ -n "$comments" ]; then
  3142 	    print "<pre>$comments</pre>"
  3143 	fi
  3145 	# Add additional comments comment
  3147 	print "<!-- Add comments to explain changes in $P here -->"
  3149 	# Add count of changes.
  3151 	if [[ -f $F.count ]]; then
  3152 	    cat $F.count
  3153 	    rm $F.count
  3154 	fi
  3155         print "</blockquote>"
  3156 done < $FLIST
  3158 print
  3159 print
  3160 print "<hr />"
  3161 print "<p style=\"font-size: small\">"
  3162 print "This code review page was prepared using <b>$0</b>"
  3163 print "(vers $WEBREV_UPDATED)."
  3164 print "</body>"
  3165 print "</html>"
  3167 if [[ -n $ZIP ]]; then
  3168     # Let's generate a zip file for convenience
  3169     cd $WDIR/..
  3170     if [ -f webrev.zip ]; then
  3171 	rm webrev.zip
  3172     fi
  3173     $ZIP -r webrev webrev >/dev/null 2>&1
  3174 fi
  3176 exec 1<&-			# Close FD 1.
  3177 exec 1<&3			# dup FD 3 to restore stdout.
  3178 exec 3<&-			# close FD 3.
  3180 print "Done."
  3181 print "Output to: $WDIR"

mercurial