make/scripts/webrev.ksh
changeset 22585 cb36782f6044
parent 22584 eed64ee05369
parent 22455 b32e2219736e
child 22586 dd49926cdfae
child 22607 ba232b417248
equal deleted inserted replaced
22584:eed64ee05369 22585:cb36782f6044
     1 #!/bin/ksh -p
       
     2 #
       
     3 # CDDL HEADER START
       
     4 #
       
     5 # The contents of this file are subject to the terms of the
       
     6 # Common Development and Distribution License (the "License").
       
     7 # You may not use this file except in compliance with the License.
       
     8 #
       
     9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
    10 # or http://www.opensolaris.org/os/licensing.
       
    11 # See the License for the specific language governing permissions
       
    12 # and limitations under the License.
       
    13 #
       
    14 # When distributing Covered Code, include this CDDL HEADER in each
       
    15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    16 # If applicable, add the following below this CDDL HEADER, with the
       
    17 # fields enclosed by brackets "[]" replaced with your own identifying
       
    18 # information: Portions Copyright [yyyy] [name of copyright owner]
       
    19 #
       
    20 # CDDL HEADER END
       
    21 #
       
    22 # Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
       
    23 # Use is subject to license terms.
       
    24 #
       
    25 # This script takes a file list and a workspace and builds a set of html files
       
    26 # suitable for doing a code review of source changes via a web page.
       
    27 # Documentation is available via 'webrev -h'.
       
    28 #
       
    29 
       
    30 WEBREV_UPDATED=25.1-hg+openjdk.java.net
       
    31 
       
    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'
       
    36 
       
    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'
       
    41 
       
    42 STDHEAD='<meta charset="utf-8">
       
    43 <meta http-equiv="cache-control" content="no-cache" />
       
    44 <meta http-equiv="Pragma" content="no-cache" />
       
    45 <meta http-equiv="Expires" content="-1" />
       
    46 <!--
       
    47    Note to customizers: the body of the webrev is IDed as SUNWwebrev
       
    48    to allow easy overriding by users of webrev via the userContent.css
       
    49    mechanism available in some browsers.
       
    50 
       
    51    For example, to have all "removed" information be red instead of
       
    52    brown, set a rule in your userContent.css file like:
       
    53 
       
    54        body#SUNWwebrev span.removed { color: red ! important; }
       
    55 -->
       
    56 <style type="text/css" media="screen">
       
    57 body {
       
    58     background-color: #eeeeee;
       
    59 }
       
    60 hr {
       
    61     border: none 0;
       
    62     border-top: 1px solid #aaa;
       
    63     height: 1px;
       
    64 }
       
    65 div.summary {
       
    66     font-size: .8em;
       
    67     border-bottom: 1px solid #aaa;
       
    68     padding-left: 1em;
       
    69     padding-right: 1em;
       
    70 }
       
    71 div.summary h2 {
       
    72     margin-bottom: 0.3em;
       
    73 }
       
    74 div.summary table th {
       
    75     text-align: right;
       
    76     vertical-align: top;
       
    77     white-space: nowrap;
       
    78 }
       
    79 span.lineschanged {
       
    80     font-size: 0.7em;
       
    81 }
       
    82 span.oldmarker {
       
    83     color: red;
       
    84     font-size: large;
       
    85     font-weight: bold;
       
    86 }
       
    87 span.newmarker {
       
    88     color: green;
       
    89     font-size: large;
       
    90     font-weight: bold;
       
    91 }
       
    92 span.removed {
       
    93     color: brown;
       
    94 }
       
    95 span.changed {
       
    96     color: blue;
       
    97 }
       
    98 span.new {
       
    99     color: blue;
       
   100     font-weight: bold;
       
   101 }
       
   102 a.print { font-size: x-small; }
       
   103 
       
   104 </style>
       
   105 
       
   106 <style type="text/css" media="print">
       
   107 pre { font-size: 0.8em; font-family: courier, monospace; }
       
   108 span.removed { color: #444; font-style: italic }
       
   109 span.changed { font-weight: bold; }
       
   110 span.new { font-weight: bold; }
       
   111 span.newmarker { font-size: 1.2em; font-weight: bold; }
       
   112 span.oldmarker { font-size: 1.2em; font-weight: bold; }
       
   113 a.print {display: none}
       
   114 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
       
   115 </style>
       
   116 '
       
   117 
       
   118 #
       
   119 # UDiffs need a slightly different CSS rule for 'new' items (we don't
       
   120 # want them to be bolded as we do in cdiffs or sdiffs).
       
   121 #
       
   122 UDIFFCSS='
       
   123 <style type="text/css" media="screen">
       
   124 span.new {
       
   125     color: blue;
       
   126     font-weight: normal;
       
   127 }
       
   128 </style>
       
   129 '
       
   130 
       
   131 #
       
   132 # input_cmd | html_quote | output_cmd
       
   133 # or
       
   134 # html_quote filename | output_cmd
       
   135 #
       
   136 # Make a piece of source code safe for display in an HTML <pre> block.
       
   137 #
       
   138 html_quote()
       
   139 {
       
   140 	sed -e "s/&/\&amp;/g" -e "s/&amp;#\([x]*[0-9A-Fa-f]\{2,5\}\);/\&#\1;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" "$@" | expand
       
   141 }
       
   142 
       
   143 #
       
   144 # input_cmd | html_quote | output_cmd
       
   145 # or
       
   146 # html_dequote filename | output_cmd
       
   147 #
       
   148 # Replace HTML entities with literals
       
   149 #
       
   150 html_dequote()
       
   151 {
       
   152 	sed -e "s/&quot;/\"/g" -e "s/&apos;/\'/g" -e "s/&amp;/\&/g" -e "s/&lt;/<'/g" -e "s/&gt;/>/g" "$@" | expand
       
   153 }
       
   154 
       
   155 #
       
   156 # input_cmd | bug2url | output_cmd
       
   157 #
       
   158 # Scan for bugids and insert <a> links to the relevent bug database.
       
   159 #
       
   160 bug2url()
       
   161 {
       
   162 	sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL$IDPREFIX'&\">&</a>|g'
       
   163 }
       
   164 
       
   165 #
       
   166 # strip_unchanged <infile> | output_cmd
       
   167 #
       
   168 # Removes chunks of sdiff documents that have not changed. This makes it
       
   169 # easier for a code reviewer to find the bits that have changed.
       
   170 #
       
   171 # Deleted lines of text are replaced by a horizontal rule. Some
       
   172 # identical lines are retained before and after the changed lines to
       
   173 # provide some context.  The number of these lines is controlled by the
       
   174 # variable C in the $AWK script below.
       
   175 #
       
   176 # The script detects changed lines as any line that has a "<span class="
       
   177 # string embedded (unchanged lines have no particular class and are not
       
   178 # part of a <span>).  Blank lines (without a sequence number) are also
       
   179 # detected since they flag lines that have been inserted or deleted.
       
   180 #
       
   181 strip_unchanged()
       
   182 {
       
   183 	$AWK '
       
   184 	BEGIN	{ C = c = 20 }
       
   185 	NF == 0 || /span class=/ {
       
   186 		if (c > C) {
       
   187 			c -= C
       
   188 			inx = 0
       
   189 			if (c > C) {
       
   190 				print "\n</pre><hr></hr><pre>"
       
   191 				inx = c % C
       
   192 				c = C
       
   193 			}
       
   194 
       
   195 			for (i = 0; i < c; i++)
       
   196 				print ln[(inx + i) % C]
       
   197 		}
       
   198 		c = 0;
       
   199 		print
       
   200 		next
       
   201 	}
       
   202 	{	if (c >= C) {
       
   203 			ln[c % C] = $0
       
   204 			c++;
       
   205 			next;
       
   206 		}
       
   207 		c++;
       
   208 		print
       
   209 	}
       
   210 	END	{ if (c > (C * 2)) print "\n</pre><hr></hr>" }
       
   211 
       
   212 	' $1
       
   213 }
       
   214 
       
   215 #
       
   216 # sdiff_to_html
       
   217 #
       
   218 # This function takes two files as arguments, obtains their diff, and
       
   219 # processes the diff output to present the files as an HTML document with
       
   220 # the files displayed side-by-side, differences shown in color.  It also
       
   221 # takes a delta comment, rendered as an HTML snippet, as the third
       
   222 # argument.  The function takes two files as arguments, then the name of
       
   223 # file, the path, and the comment.  The HTML will be delivered on stdout,
       
   224 # e.g.
       
   225 #
       
   226 #   $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
       
   227 #         new/usr/src/tools/scripts/webrev.sh \
       
   228 #         webrev.sh usr/src/tools/scripts \
       
   229 #         '<a href="https://bugs.openjdk.java.net/browse/JDK-1234567">
       
   230 #          JDK-1234567</a> my bugid' > <file>.html
       
   231 #
       
   232 # framed_sdiff() is then called which creates $2.frames.html
       
   233 # in the webrev tree.
       
   234 #
       
   235 # FYI: This function is rather unusual in its use of awk.  The initial
       
   236 # diff run produces conventional diff output showing changed lines mixed
       
   237 # with editing codes.  The changed lines are ignored - we're interested in
       
   238 # the editing codes, e.g.
       
   239 #
       
   240 #      8c8
       
   241 #      57a61
       
   242 #      63c66,76
       
   243 #      68,93d80
       
   244 #      106d90
       
   245 #      108,110d91
       
   246 #
       
   247 #  These editing codes are parsed by the awk script and used to generate
       
   248 #  another awk script that generates HTML, e.g the above lines would turn
       
   249 #  into something like this:
       
   250 #
       
   251 #      BEGIN { printf "<pre>\n" }
       
   252 #      function sp(n) {for (i=0;i<n;i++)printf "\n"}
       
   253 #      function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
       
   254 #      NR==8           {wl("#7A7ADD");next}
       
   255 #      NR==54          {wl("#7A7ADD");sp(3);next}
       
   256 #      NR==56          {wl("#7A7ADD");next}
       
   257 #      NR==57          {wl("black");printf "\n"; next}
       
   258 #        :               :
       
   259 #
       
   260 #  This script is then run on the original source file to generate the
       
   261 #  HTML that corresponds to the source file.
       
   262 #
       
   263 #  The two HTML files are then combined into a single piece of HTML that
       
   264 #  uses an HTML table construct to present the files side by side.  You'll
       
   265 #  notice that the changes are color-coded:
       
   266 #
       
   267 #   black     - unchanged lines
       
   268 #   blue      - changed lines
       
   269 #   bold blue - new lines
       
   270 #   brown     - deleted lines
       
   271 #
       
   272 #  Blank lines are inserted in each file to keep unchanged lines in sync
       
   273 #  (side-by-side).  This format is familiar to users of sdiff(1) or
       
   274 #  Teamware's filemerge tool.
       
   275 #
       
   276 sdiff_to_html()
       
   277 {
       
   278 	diff -b $1 $2 > /tmp/$$.diffs
       
   279 
       
   280 	TNAME=$3
       
   281 	TPATH=$4
       
   282 	COMMENT=$5
       
   283 
       
   284 	#
       
   285 	#  Now we have the diffs, generate the HTML for the old file.
       
   286 	#
       
   287 	$AWK '
       
   288 	BEGIN	{
       
   289 		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
       
   290 		printf "function removed() "
       
   291 		printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
       
   292 		printf "function changed() "
       
   293 		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
       
   294 		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
       
   295 }
       
   296 	/^</	{next}
       
   297 	/^>/	{next}
       
   298 	/^---/	{next}
       
   299 
       
   300 	{
       
   301 	split($1, a, /[cad]/) ;
       
   302 	if (index($1, "a")) {
       
   303 		if (a[1] == 0) {
       
   304 			n = split(a[2], r, /,/);
       
   305 			if (n == 1)
       
   306 				printf "BEGIN\t\t{sp(1)}\n"
       
   307 			else
       
   308 				printf "BEGIN\t\t{sp(%d)}\n",\
       
   309 				(r[2] - r[1]) + 1
       
   310 			next
       
   311 		}
       
   312 
       
   313 		printf "NR==%s\t\t{", a[1]
       
   314 		n = split(a[2], r, /,/);
       
   315 		s = r[1];
       
   316 		if (n == 1)
       
   317 			printf "bl();printf \"\\n\"; next}\n"
       
   318 		else {
       
   319 			n = r[2] - r[1]
       
   320 			printf "bl();sp(%d);next}\n",\
       
   321 			(r[2] - r[1]) + 1
       
   322 		}
       
   323 		next
       
   324 	}
       
   325 	if (index($1, "d")) {
       
   326 		n = split(a[1], r, /,/);
       
   327 		n1 = r[1]
       
   328 		n2 = r[2]
       
   329 		if (n == 1)
       
   330 			printf "NR==%s\t\t{removed(); next}\n" , n1
       
   331 		else
       
   332 			printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
       
   333 		next
       
   334 	}
       
   335 	if (index($1, "c")) {
       
   336 		n = split(a[1], r, /,/);
       
   337 		n1 = r[1]
       
   338 		n2 = r[2]
       
   339 		final = n2
       
   340 		d1 = 0
       
   341 		if (n == 1)
       
   342 			printf "NR==%s\t\t{changed();" , n1
       
   343 		else {
       
   344 			d1 = n2 - n1
       
   345 			printf "NR==%s,NR==%s\t{changed();" , n1, n2
       
   346 		}
       
   347 		m = split(a[2], r, /,/);
       
   348 		n1 = r[1]
       
   349 		n2 = r[2]
       
   350 		if (m > 1) {
       
   351 			d2  = n2 - n1
       
   352 			if (d2 > d1) {
       
   353 				if (n > 1) printf "if (NR==%d)", final
       
   354 				printf "sp(%d);", d2 - d1
       
   355 			}
       
   356 		}
       
   357 		printf "next}\n" ;
       
   358 
       
   359 		next
       
   360 	}
       
   361 	}
       
   362 
       
   363 	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
       
   364 	' /tmp/$$.diffs > /tmp/$$.file1
       
   365 
       
   366 	#
       
   367 	#  Now generate the HTML for the new file
       
   368 	#
       
   369 	$AWK '
       
   370 	BEGIN	{
       
   371 		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
       
   372 		printf "function new() "
       
   373 		printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
       
   374 		printf "function changed() "
       
   375 		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
       
   376 		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
       
   377 	}
       
   378 
       
   379 	/^</	{next}
       
   380 	/^>/	{next}
       
   381 	/^---/	{next}
       
   382 
       
   383 	{
       
   384 	split($1, a, /[cad]/) ;
       
   385 	if (index($1, "d")) {
       
   386 		if (a[2] == 0) {
       
   387 			n = split(a[1], r, /,/);
       
   388 			if (n == 1)
       
   389 				printf "BEGIN\t\t{sp(1)}\n"
       
   390 			else
       
   391 				printf "BEGIN\t\t{sp(%d)}\n",\
       
   392 				(r[2] - r[1]) + 1
       
   393 			next
       
   394 		}
       
   395 
       
   396 		printf "NR==%s\t\t{", a[2]
       
   397 		n = split(a[1], r, /,/);
       
   398 		s = r[1];
       
   399 		if (n == 1)
       
   400 			printf "bl();printf \"\\n\"; next}\n"
       
   401 		else {
       
   402 			n = r[2] - r[1]
       
   403 			printf "bl();sp(%d);next}\n",\
       
   404 			(r[2] - r[1]) + 1
       
   405 		}
       
   406 		next
       
   407 	}
       
   408 	if (index($1, "a")) {
       
   409 		n = split(a[2], r, /,/);
       
   410 		n1 = r[1]
       
   411 		n2 = r[2]
       
   412 		if (n == 1)
       
   413 			printf "NR==%s\t\t{new() ; next}\n" , n1
       
   414 		else
       
   415 			printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
       
   416 		next
       
   417 	}
       
   418 	if (index($1, "c")) {
       
   419 		n = split(a[2], r, /,/);
       
   420 		n1 = r[1]
       
   421 		n2 = r[2]
       
   422 		final = n2
       
   423 		d2 = 0;
       
   424 		if (n == 1) {
       
   425 			final = n1
       
   426 			printf "NR==%s\t\t{changed();" , n1
       
   427 		} else {
       
   428 			d2 = n2 - n1
       
   429 			printf "NR==%s,NR==%s\t{changed();" , n1, n2
       
   430 		}
       
   431 		m = split(a[1], r, /,/);
       
   432 		n1 = r[1]
       
   433 		n2 = r[2]
       
   434 		if (m > 1) {
       
   435 			d1  = n2 - n1
       
   436 			if (d1 > d2) {
       
   437 				if (n > 1) printf "if (NR==%d)", final
       
   438 				printf "sp(%d);", d1 - d2
       
   439 			}
       
   440 		}
       
   441 		printf "next}\n" ;
       
   442 		next
       
   443 	}
       
   444 	}
       
   445 	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
       
   446 	' /tmp/$$.diffs > /tmp/$$.file2
       
   447 
       
   448 	#
       
   449 	# Post-process the HTML files by running them back through $AWK
       
   450 	#
       
   451 	html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
       
   452 
       
   453 	html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
       
   454 
       
   455 	#
       
   456 	# Now combine into a valid HTML file and side-by-side into a table
       
   457 	#
       
   458 	print "$HTML<head>$STDHEAD"
       
   459 	print "<title>$WNAME Sdiff $TPATH </title>"
       
   460 	print "</head><body id=\"SUNWwebrev\">"
       
   461 	print "<h2>$TPATH/$TNAME</h2>"
       
   462         print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
       
   463 	print "<pre>$COMMENT</pre>\n"
       
   464 	print "<table><tr valign=\"top\">"
       
   465 	print "<td><pre>"
       
   466 
       
   467 	strip_unchanged /tmp/$$.file1.html
       
   468 
       
   469 	print "</pre></td><td><pre>"
       
   470 
       
   471 	strip_unchanged /tmp/$$.file2.html
       
   472 
       
   473 	print "</pre></td>"
       
   474 	print "</tr></table>"
       
   475 	print "</body></html>"
       
   476 
       
   477 	framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
       
   478 	    "$COMMENT"
       
   479 }
       
   480 
       
   481 
       
   482 #
       
   483 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
       
   484 #
       
   485 # Expects lefthand and righthand side html files created by sdiff_to_html.
       
   486 # We use insert_anchors() to augment those with HTML navigation anchors,
       
   487 # and then emit the main frame.  Content is placed into:
       
   488 #
       
   489 #    $WDIR/DIR/$TNAME.lhs.html
       
   490 #    $WDIR/DIR/$TNAME.rhs.html
       
   491 #    $WDIR/DIR/$TNAME.frames.html
       
   492 #
       
   493 # NOTE: We rely on standard usage of $WDIR and $DIR.
       
   494 #
       
   495 function framed_sdiff
       
   496 {
       
   497 	typeset TNAME=$1
       
   498 	typeset TPATH=$2
       
   499 	typeset lhsfile=$3
       
   500 	typeset rhsfile=$4
       
   501 	typeset comments=$5
       
   502 	typeset RTOP
       
   503 
       
   504 	# Enable html files to access WDIR via a relative path.
       
   505 	RTOP=$(relative_dir $TPATH $WDIR)
       
   506 
       
   507 	# Make the rhs/lhs files and output the frameset file.
       
   508 	print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
       
   509 
       
   510 	cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
       
   511 	    <script type="text/javascript" src="$RTOP/ancnav.js"></script>
       
   512 	    </head>
       
   513 	    <body id="SUNWwebrev" onkeypress="keypress(event);">
       
   514 	    <a name="0"></a>
       
   515 	    <pre>$comments</pre><hr></hr>
       
   516 	EOF
       
   517 
       
   518 	cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
       
   519 
       
   520 	insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
       
   521 	insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
       
   522 
       
   523 	close='</body></html>'
       
   524 
       
   525 	print $close >> $WDIR/$DIR/$TNAME.lhs.html
       
   526 	print $close >> $WDIR/$DIR/$TNAME.rhs.html
       
   527 
       
   528 	print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
       
   529 	print "<title>$WNAME Framed-Sdiff " \
       
   530 	    "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
       
   531 	cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
       
   532 	  <frameset rows="*,60">
       
   533 	    <frameset cols="50%,50%">
       
   534 	      <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs" />
       
   535 	      <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs" />
       
   536 	    </frameset>
       
   537 	  <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0"
       
   538 	   marginheight="0" name="nav" />
       
   539 	  <noframes>
       
   540             <body id="SUNWwebrev">
       
   541 	      Alas 'frames' webrev requires that your browser supports frames
       
   542 	      and has the feature enabled.
       
   543             </body>
       
   544 	  </noframes>
       
   545 	  </frameset>
       
   546 	</html>
       
   547 	EOF
       
   548 }
       
   549 
       
   550 
       
   551 #
       
   552 # fix_postscript
       
   553 #
       
   554 # Merge codereview output files to a single conforming postscript file, by:
       
   555 # 	- removing all extraneous headers/trailers
       
   556 #	- making the page numbers right
       
   557 #	- removing pages devoid of contents which confuse some
       
   558 #	  postscript readers.
       
   559 #
       
   560 # From Casper.
       
   561 #
       
   562 function fix_postscript
       
   563 {
       
   564 	infile=$1
       
   565 
       
   566 	cat > /tmp/$$.crmerge.pl << \EOF
       
   567 
       
   568 	print scalar(<>);		# %!PS-Adobe---
       
   569 	print "%%Orientation: Landscape\n";
       
   570 
       
   571 	$pno = 0;
       
   572 	$doprint = 1;
       
   573 
       
   574 	$page = "";
       
   575 
       
   576 	while (<>) {
       
   577 		next if (/^%%Pages:\s*\d+/);
       
   578 
       
   579 		if (/^%%Page:/) {
       
   580 			if ($pno == 0 || $page =~ /\)S/) {
       
   581 				# Header or single page containing text
       
   582 				print "%%Page: ? $pno\n" if ($pno > 0);
       
   583 				print $page;
       
   584 				$pno++;
       
   585 			} else {
       
   586 				# Empty page, skip it.
       
   587 			}
       
   588 			$page = "";
       
   589 			$doprint = 1;
       
   590 			next;
       
   591 		}
       
   592 
       
   593 		# Skip from %%Trailer of one document to Endprolog
       
   594 		# %%Page of the next
       
   595 		$doprint = 0 if (/^%%Trailer/);
       
   596 		$page .= $_ if ($doprint);
       
   597 	}
       
   598 
       
   599 	if ($page =~ /\)S/) {
       
   600 		print "%%Page: ? $pno\n";
       
   601 		print $page;
       
   602 	} else {
       
   603 		$pno--;
       
   604 	}
       
   605 	print "%%Trailer\n%%Pages: $pno\n";
       
   606 EOF
       
   607 
       
   608 	$PERL /tmp/$$.crmerge.pl < $infile
       
   609 }
       
   610 
       
   611 
       
   612 #
       
   613 # input_cmd | insert_anchors | output_cmd
       
   614 #
       
   615 # Flag blocks of difference with sequentially numbered invisible
       
   616 # anchors.  These are used to drive the frames version of the
       
   617 # sdiffs output.
       
   618 #
       
   619 # NOTE: Anchor zero flags the top of the file irrespective of changes,
       
   620 # an additional anchor is also appended to flag the bottom.
       
   621 #
       
   622 # The script detects changed lines as any line that has a "<span
       
   623 # class=" string embedded (unchanged lines have no class set and are
       
   624 # not part of a <span>.  Blank lines (without a sequence number)
       
   625 # are also detected since they flag lines that have been inserted or
       
   626 # deleted.
       
   627 #
       
   628 function insert_anchors
       
   629 {
       
   630 	$AWK '
       
   631 	function ia() {
       
   632 		# This should be able to be a singleton <a /> but that
       
   633 		# seems to trigger a bug in firefox a:hover rule processing
       
   634 		printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
       
   635 	}
       
   636 
       
   637 	BEGIN {
       
   638 		anc=1;
       
   639 		inblock=1;
       
   640 		printf "<pre>\n";
       
   641 	}
       
   642 	NF == 0 || /^<span class=/ {
       
   643 		if (inblock == 0) {
       
   644 			ia();
       
   645 			inblock=1;
       
   646 		}
       
   647 		print;
       
   648 		next;
       
   649 	}
       
   650 	{
       
   651 		inblock=0;
       
   652 		print;
       
   653 	}
       
   654 	END {
       
   655 		ia();
       
   656 
       
   657 		printf "<b style=\"font-size: large; color: red\">";
       
   658 		printf "--- EOF ---</b>"
       
   659         	for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
       
   660 		printf "</pre>"
       
   661 		printf "<form name=\"eof\">";
       
   662 		printf "<input name=\"value\" value=\"%d\" type=\"hidden\" />",
       
   663 		    anc - 1;
       
   664 		printf "</form>";
       
   665 	}
       
   666 	' $1
       
   667 }
       
   668 
       
   669 
       
   670 #
       
   671 # relative_dir
       
   672 #
       
   673 # Print a relative return path from $1 to $2.  For example if
       
   674 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
       
   675 # this function would print "../../../../".
       
   676 #
       
   677 # In the event that $1 is not in $2 a warning is printed to stderr,
       
   678 # and $2 is returned-- the result of this is that the resulting webrev
       
   679 # is not relocatable.
       
   680 #
       
   681 function relative_dir
       
   682 {
       
   683     d1=$1
       
   684     d2=$2
       
   685     if [[ "$d1" == "." ]]; then
       
   686 	print "."
       
   687     else
       
   688 	typeset cur="${d1##$d2?(/)}"
       
   689 	typeset ret=""
       
   690 	if [[ $d2 == $cur ]]; then   # Should never happen.
       
   691 		# Should never happen.
       
   692 		print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
       
   693 		print -u2 "to \"$2\".  Check input paths.  Framed webrev "
       
   694 		print -u2 "will not be relocatable!"
       
   695 		print $2
       
   696 		return
       
   697 	fi
       
   698 
       
   699 	while [[ -n ${cur} ]];
       
   700 	do
       
   701 		cur=${cur%%*(/)*([!/])}
       
   702 		if [[ -z $ret ]]; then
       
   703 			ret=".."
       
   704 		else
       
   705 			ret="../$ret"
       
   706 		fi
       
   707 	done
       
   708 	print $ret
       
   709     fi
       
   710 }
       
   711 
       
   712 
       
   713 #
       
   714 # frame_nav_js
       
   715 #
       
   716 # Emit javascript for frame navigation
       
   717 #
       
   718 function frame_nav_js
       
   719 {
       
   720 cat << \EOF
       
   721 var myInt;
       
   722 var scrolling=0;
       
   723 var sfactor = 3;
       
   724 var scount=10;
       
   725 
       
   726 function scrollByPix() {
       
   727 	if (scount<=0) {
       
   728 		sfactor*=1.2;
       
   729 		scount=10;
       
   730 	}
       
   731 	parent.lhs.scrollBy(0,sfactor);
       
   732 	parent.rhs.scrollBy(0,sfactor);
       
   733 	scount--;
       
   734 }
       
   735 
       
   736 function scrollToAnc(num) {
       
   737 
       
   738 	// Update the value of the anchor in the form which we use as
       
   739 	// storage for this value.  setAncValue() will take care of
       
   740 	// correcting for overflow and underflow of the value and return
       
   741 	// us the new value.
       
   742 	num = setAncValue(num);
       
   743 
       
   744 	// Set location and scroll back a little to expose previous
       
   745 	// lines.
       
   746 	//
       
   747 	// Note that this could be improved: it is possible although
       
   748 	// complex to compute the x and y position of an anchor, and to
       
   749 	// scroll to that location directly.
       
   750 	//
       
   751 	parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
       
   752 	parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
       
   753 
       
   754 	parent.lhs.scrollBy(0,-30);
       
   755 	parent.rhs.scrollBy(0,-30);
       
   756 }
       
   757 
       
   758 function getAncValue()
       
   759 {
       
   760 	return (parseInt(parent.nav.document.diff.real.value));
       
   761 }
       
   762 
       
   763 function setAncValue(val)
       
   764 {
       
   765 	if (val <= 0) {
       
   766 		val = 0;
       
   767 		parent.nav.document.diff.real.value = val;
       
   768 		parent.nav.document.diff.display.value = "BOF";
       
   769 		return (val);
       
   770 	}
       
   771 
       
   772 	//
       
   773 	// The way we compute the max anchor value is to stash it
       
   774 	// inline in the left and right hand side pages-- it's the same
       
   775 	// on each side, so we pluck from the left.
       
   776 	//
       
   777 	maxval = parent.lhs.document.eof.value.value;
       
   778 	if (val < maxval) {
       
   779 		parent.nav.document.diff.real.value = val;
       
   780 		parent.nav.document.diff.display.value = val.toString();
       
   781 		return (val);
       
   782 	}
       
   783 
       
   784 	// this must be: val >= maxval
       
   785 	val = maxval;
       
   786 	parent.nav.document.diff.real.value = val;
       
   787 	parent.nav.document.diff.display.value = "EOF";
       
   788 	return (val);
       
   789 }
       
   790 
       
   791 function stopScroll() {
       
   792 	if (scrolling==1) {
       
   793 		clearInterval(myInt);
       
   794 		scrolling=0;
       
   795 	}
       
   796 }
       
   797 
       
   798 function startScroll() {
       
   799 	stopScroll();
       
   800 	scrolling=1;
       
   801 	myInt=setInterval("scrollByPix()",10);
       
   802 }
       
   803 
       
   804 function handlePress(b) {
       
   805 
       
   806 	switch (b) {
       
   807 	    case 1 :
       
   808 		scrollToAnc(-1);
       
   809 		break;
       
   810 	    case 2 :
       
   811 		scrollToAnc(getAncValue() - 1);
       
   812 		break;
       
   813 	    case 3 :
       
   814 		sfactor=-3;
       
   815 		startScroll();
       
   816 		break;
       
   817 	    case 4 :
       
   818 		sfactor=3;
       
   819 		startScroll();
       
   820 		break;
       
   821 	    case 5 :
       
   822 		scrollToAnc(getAncValue() + 1);
       
   823 		break;
       
   824 	    case 6 :
       
   825 		scrollToAnc(999999);
       
   826 		break;
       
   827 	}
       
   828 }
       
   829 
       
   830 function handleRelease(b) {
       
   831 	stopScroll();
       
   832 }
       
   833 
       
   834 function keypress(ev) {
       
   835 	var keynum;
       
   836 	var keychar;
       
   837 
       
   838 	if (window.event) { // IE
       
   839 		keynum = ev.keyCode;
       
   840 	} else if (ev.which) { // non-IE
       
   841 		keynum = ev.which;
       
   842 	}
       
   843 
       
   844 	keychar = String.fromCharCode(keynum);
       
   845 
       
   846 	if (keychar == "k") {
       
   847 		handlePress(2);
       
   848 		return (0);
       
   849 	} else if (keychar == "j" || keychar == " ") {
       
   850 		handlePress(5);
       
   851 		return (0);
       
   852 	}
       
   853 	return (1);
       
   854 }
       
   855 
       
   856 function ValidateDiffNum(){
       
   857 	val = parent.nav.document.diff.display.value;
       
   858 	if (val == "EOF") {
       
   859 		scrollToAnc(999999);
       
   860 		return;
       
   861 	}
       
   862 
       
   863 	if (val == "BOF") {
       
   864 		scrollToAnc(0);
       
   865 		return;
       
   866 	}
       
   867 
       
   868         i=parseInt(val);
       
   869         if (isNaN(i)) {
       
   870                 parent.nav.document.diff.display.value = getAncValue();
       
   871         } else {
       
   872                 scrollToAnc(i);
       
   873         }
       
   874         return false;
       
   875 }
       
   876 
       
   877 EOF
       
   878 }
       
   879 
       
   880 #
       
   881 # frame_navigation
       
   882 #
       
   883 # Output anchor navigation file for framed sdiffs.
       
   884 #
       
   885 function frame_navigation
       
   886 {
       
   887 	print "$HTML<head>$STDHEAD"
       
   888 
       
   889 	cat << \EOF
       
   890 <title>Anchor Navigation</title>
       
   891 <meta http-equiv="Content-Script-Type" content="text/javascript" />
       
   892 <meta http-equiv="Content-Type" content="text/html" />
       
   893 
       
   894 <style type="text/css">
       
   895     div.button td { padding-left: 5px; padding-right: 5px;
       
   896 		    background-color: #eee; text-align: center;
       
   897 		    border: 1px #444 outset; cursor: pointer; }
       
   898     div.button a { font-weight: bold; color: black }
       
   899     div.button td:hover { background: #ffcc99; }
       
   900 </style>
       
   901 EOF
       
   902 
       
   903 	print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
       
   904 
       
   905 	cat << \EOF
       
   906 </head>
       
   907 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
       
   908 	onkeypress="keypress(event);">
       
   909     <noscript lang="javascript">
       
   910       <center>
       
   911 	<p><big>Framed Navigation controls require Javascript</big><br />
       
   912 	Either this browser is incompatable or javascript is not enabled</p>
       
   913       </center>
       
   914     </noscript>
       
   915     <table width="100%" border="0" align="center">
       
   916 	<tr>
       
   917           <td valign="middle" width="25%">Diff navigation:
       
   918           Use 'j' and 'k' for next and previous diffs; or use buttons
       
   919           at right</td>
       
   920 	  <td align="center" valign="top" width="50%">
       
   921 	    <div class="button">
       
   922 	      <table border="0" align="center">
       
   923                   <tr>
       
   924 		    <td>
       
   925 		      <a onMouseDown="handlePress(1);return true;"
       
   926 			 onMouseUp="handleRelease(1);return true;"
       
   927 			 onMouseOut="handleRelease(1);return true;"
       
   928 			 onClick="return false;"
       
   929 			 title="Go to Beginning Of file">BOF</a></td>
       
   930 		    <td>
       
   931 		      <a onMouseDown="handlePress(3);return true;"
       
   932 			 onMouseUp="handleRelease(3);return true;"
       
   933 			 onMouseOut="handleRelease(3);return true;"
       
   934 			 title="Scroll Up: Press and Hold to accelerate"
       
   935 			 onClick="return false;">Scroll Up</a></td>
       
   936 		    <td>
       
   937 		      <a onMouseDown="handlePress(2);return true;"
       
   938 			 onMouseUp="handleRelease(2);return true;"
       
   939 			 onMouseOut="handleRelease(2);return true;"
       
   940 			 title="Go to previous Diff"
       
   941 			 onClick="return false;">Prev Diff</a>
       
   942 		    </td></tr>
       
   943 
       
   944 		  <tr>
       
   945 		    <td>
       
   946 		      <a onMouseDown="handlePress(6);return true;"
       
   947 			 onMouseUp="handleRelease(6);return true;"
       
   948 			 onMouseOut="handleRelease(6);return true;"
       
   949 			 onClick="return false;"
       
   950 			 title="Go to End Of File">EOF</a></td>
       
   951 		    <td>
       
   952 		      <a onMouseDown="handlePress(4);return true;"
       
   953 			 onMouseUp="handleRelease(4);return true;"
       
   954 			 onMouseOut="handleRelease(4);return true;"
       
   955 			 title="Scroll Down: Press and Hold to accelerate"
       
   956 			 onClick="return false;">Scroll Down</a></td>
       
   957 		    <td>
       
   958 		      <a onMouseDown="handlePress(5);return true;"
       
   959 			 onMouseUp="handleRelease(5);return true;"
       
   960 			 onMouseOut="handleRelease(5);return true;"
       
   961 			 title="Go to next Diff"
       
   962 			 onClick="return false;">Next Diff</a></td>
       
   963 		  </tr>
       
   964               </table>
       
   965 	    </div>
       
   966 	  </td>
       
   967 	  <th valign="middle" width="25%">
       
   968 	    <form action="" name="diff" onsubmit="return ValidateDiffNum();">
       
   969 		<input name="display" value="BOF" size="8" type="text" />
       
   970 		<input name="real" value="0" size="8" type="hidden" />
       
   971 	    </form>
       
   972 	  </th>
       
   973 	</tr>
       
   974     </table>
       
   975   </body>
       
   976 </html>
       
   977 EOF
       
   978 }
       
   979 
       
   980 
       
   981 
       
   982 #
       
   983 # diff_to_html <filename> <filepath> { U | C } <comment>
       
   984 #
       
   985 # Processes the output of diff to produce an HTML file representing either
       
   986 # context or unified diffs.
       
   987 #
       
   988 diff_to_html()
       
   989 {
       
   990 	TNAME=$1
       
   991 	TPATH=$2
       
   992 	DIFFTYPE=$3
       
   993 	COMMENT=$4
       
   994 
       
   995 	print "$HTML<head>$STDHEAD"
       
   996 	print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
       
   997 
       
   998 	if [[ $DIFFTYPE == "U" ]]; then
       
   999 		print "$UDIFFCSS"
       
  1000 	fi
       
  1001 
       
  1002 	cat <<-EOF
       
  1003 	</head>
       
  1004 	<body id="SUNWwebrev">
       
  1005 	<h2>$TPATH</h2>
       
  1006         <a class="print" href="javascript:print()">Print this page</a>
       
  1007 	<pre>$COMMENT</pre>
       
  1008         <pre>
       
  1009 EOF
       
  1010 
       
  1011 	html_quote | $AWK '
       
  1012 	/^--- new/	{ next }
       
  1013 	/^\+\+\+ new/	{ next }
       
  1014 	/^--- old/	{ next }
       
  1015 	/^\*\*\* old/	{ next }
       
  1016 	/^\*\*\*\*/	{ next }
       
  1017 	/^-------/	{ printf "<center><h1>%s</h1></center>\n", $0; next }
       
  1018 	/^\@\@.*\@\@$/	{ printf "</pre><hr /><pre>\n";
       
  1019 			  printf "<span class=\"newmarker\">%s</span>\n", $0;
       
  1020 			  next}
       
  1021 
       
  1022 	/^\*\*\*/	{ printf "<hr /><span class=\"oldmarker\">%s</span>\n", $0;
       
  1023 			  next}
       
  1024 	/^---/		{ printf "<span class=\"newmarker\">%s</span>\n", $0;
       
  1025 			  next}
       
  1026 	/^\+/		{printf "<span class=\"new\">%s</span>\n", $0; next}
       
  1027 	/^!/		{printf "<span class=\"changed\">%s</span>\n", $0; next}
       
  1028 	/^-/		{printf "<span class=\"removed\">%s</span>\n", $0; next}
       
  1029 			{printf "%s\n", $0; next}
       
  1030 	'
       
  1031 
       
  1032 	print "</pre></body></html>\n"
       
  1033 }
       
  1034 
       
  1035 
       
  1036 #
       
  1037 # source_to_html { new | old } <filename>
       
  1038 #
       
  1039 # Process a plain vanilla source file to transform it into an HTML file.
       
  1040 #
       
  1041 source_to_html()
       
  1042 {
       
  1043 	WHICH=$1
       
  1044 	TNAME=$2
       
  1045 
       
  1046 	print "$HTML<head>$STDHEAD"
       
  1047 	print "<title>$WHICH $TNAME</title>"
       
  1048 	print "<body id=\"SUNWwebrev\">"
       
  1049 	print "<pre>"
       
  1050 	html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
       
  1051 	print "</pre></body></html>"
       
  1052 }
       
  1053 
       
  1054 comments_from_mercurial()
       
  1055 {
       
  1056 	fmt=$1
       
  1057 	pfile=$PWS/$2
       
  1058 	cfile=$CWS/$3
       
  1059 
       
  1060         logdir=`dirname $cfile`
       
  1061         logf=`basename $cfile`
       
  1062         if [ -d $logdir ]; then
       
  1063             ( cd $logdir;
       
  1064 	        active=`hg status $logf 2>/dev/null`
       
  1065                 # If the output from 'hg status' is not empty, it means the file
       
  1066                 # hasn't been committed, so don't fetch comments.
       
  1067 	        if [[ -z $active ]] ; then
       
  1068                     if [[ -n $ALL_CREV ]]; then
       
  1069                         rev_opt=
       
  1070                         for rev in $ALL_CREV; do
       
  1071                             rev_opt="$rev_opt --rev $rev"
       
  1072                         done
       
  1073                         comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf`
       
  1074                     elif [[ -n $FIRST_CREV ]]; then
       
  1075 		        comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf`
       
  1076                     else
       
  1077 		        comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf`
       
  1078                     fi
       
  1079 	        else
       
  1080 	            comm=""
       
  1081 	        fi
       
  1082 	        if [[ $fmt == "text" ]]; then
       
  1083 	            print "$comm"
       
  1084 	            return
       
  1085 	        fi
       
  1086 
       
  1087 	        print "$comm" | html_quote | bug2url
       
  1088                 )
       
  1089         fi
       
  1090 }
       
  1091 
       
  1092 
       
  1093 #
       
  1094 # getcomments {text|html} filepath parentpath
       
  1095 #
       
  1096 # Fetch the comments depending on what SCM mode we're in.
       
  1097 #
       
  1098 getcomments()
       
  1099 {
       
  1100 	typeset fmt=$1
       
  1101 	typeset p=$2
       
  1102 	typeset pp=$3
       
  1103 
       
  1104 	comments_from_mercurial $fmt $pp $p
       
  1105 }
       
  1106 
       
  1107 #
       
  1108 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
       
  1109 #
       
  1110 # Print out Code Inspection figures similar to sccs-prt(1) format.
       
  1111 #
       
  1112 function printCI
       
  1113 {
       
  1114 	integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
       
  1115 	typeset str
       
  1116 	if (( tot == 1 )); then
       
  1117 		str="line"
       
  1118 	else
       
  1119 		str="lines"
       
  1120 	fi
       
  1121 	printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \
       
  1122 	    $tot $str $ins $del $mod $unc
       
  1123 }
       
  1124 
       
  1125 
       
  1126 #
       
  1127 # difflines <oldfile> <newfile>
       
  1128 #
       
  1129 # Calculate and emit number of added, removed, modified and unchanged lines,
       
  1130 # and total lines changed, the sum of added + removed + modified.
       
  1131 #
       
  1132 function difflines
       
  1133 {
       
  1134 	integer tot mod del ins unc err
       
  1135 	typeset filename
       
  1136 
       
  1137 	eval $( diff -e $1 $2 | $AWK '
       
  1138 	# Change range of lines: N,Nc
       
  1139 	/^[0-9]*,[0-9]*c$/ {
       
  1140 		n=split(substr($1,1,length($1)-1), counts, ",");
       
  1141 		if (n != 2) {
       
  1142 		    error=2
       
  1143 		    exit;
       
  1144 		}
       
  1145 		#
       
  1146 		# 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
       
  1147 		# following would be 5 - 3 = 2! Hence +1 for correction.
       
  1148 		#
       
  1149 		r=(counts[2]-counts[1])+1;
       
  1150 
       
  1151 		#
       
  1152 		# Now count replacement lines: each represents a change instead
       
  1153 		# of a delete, so increment c and decrement r.
       
  1154 		#
       
  1155 		while (getline != /^\.$/) {
       
  1156 			c++;
       
  1157 			r--;
       
  1158 		}
       
  1159 		#
       
  1160 		# If there were more replacement lines than original lines,
       
  1161 		# then r will be negative; in this case there are no deletions,
       
  1162 		# but there are r changes that should be counted as adds, and
       
  1163 		# since r is negative, subtract it from a and add it to c.
       
  1164 		#
       
  1165 		if (r < 0) {
       
  1166 			a-=r;
       
  1167 			c+=r;
       
  1168 		}
       
  1169 
       
  1170 		#
       
  1171 		# If there were more original lines than replacement lines, then
       
  1172 		# r will be positive; in this case, increment d by that much.
       
  1173 		#
       
  1174 		if (r > 0) {
       
  1175 			d+=r;
       
  1176 		}
       
  1177 		next;
       
  1178 	}
       
  1179 
       
  1180 	# Change lines: Nc
       
  1181 	/^[0-9].*c$/ {
       
  1182 		# The first line is a replacement; any more are additions.
       
  1183 		if (getline != /^\.$/) {
       
  1184 			c++;
       
  1185 			while (getline != /^\.$/) a++;
       
  1186 		}
       
  1187 		next;
       
  1188 	}
       
  1189 
       
  1190 	# Add lines: both Na and N,Na
       
  1191 	/^[0-9].*a$/ {
       
  1192 		while (getline != /^\.$/) a++;
       
  1193 		next;
       
  1194 	}
       
  1195 
       
  1196 	# Delete range of lines: N,Nd
       
  1197 	/^[0-9]*,[0-9]*d$/ {
       
  1198 		n=split(substr($1,1,length($1)-1), counts, ",");
       
  1199 		if (n != 2) {
       
  1200 			error=2
       
  1201 			exit;
       
  1202 		}
       
  1203 		#
       
  1204 		# 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
       
  1205 		# following would be 5 - 3 = 2! Hence +1 for correction.
       
  1206 		#
       
  1207 		r=(counts[2]-counts[1])+1;
       
  1208 		d+=r;
       
  1209 		next;
       
  1210 	}
       
  1211 
       
  1212 	# Delete line: Nd.   For example 10d says line 10 is deleted.
       
  1213 	/^[0-9]*d$/ {d++; next}
       
  1214 
       
  1215 	# Should not get here!
       
  1216 	{
       
  1217 		error=1;
       
  1218 		exit;
       
  1219 	}
       
  1220 
       
  1221 	# Finish off - print results
       
  1222 	END {
       
  1223 		printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
       
  1224 		    (c+d+a), c, d, a, error);
       
  1225 	}' )
       
  1226 
       
  1227 	# End of $AWK, Check to see if any trouble occurred.
       
  1228 	if (( $? > 0 || err > 0 )); then
       
  1229 		print "Unexpected Error occurred reading" \
       
  1230 		    "\`diff -e $1 $2\`: \$?=$?, err=" $err
       
  1231 		return
       
  1232 	fi
       
  1233 
       
  1234 	# Accumulate totals
       
  1235 	(( TOTL += tot ))
       
  1236 	(( TMOD += mod ))
       
  1237 	(( TDEL += del ))
       
  1238 	(( TINS += ins ))
       
  1239 	# Calculate unchanged lines
       
  1240 	unc=`wc -l < $1`
       
  1241 	if (( unc > 0 )); then
       
  1242 		(( unc -= del + mod ))
       
  1243 		(( TUNC += unc ))
       
  1244 	fi
       
  1245 	# print summary
       
  1246 	print "<span class=\"lineschanged\">\c"
       
  1247 	printCI $tot $ins $del $mod $unc
       
  1248 	print "</span>"
       
  1249 }
       
  1250 
       
  1251 function outgoing_from_mercurial_forest
       
  1252 {
       
  1253     hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK '
       
  1254         BEGIN           {ntree=0}
       
  1255         /^comparing/    {next}
       
  1256         /^no changes/   {next}
       
  1257         /^searching/    {next}
       
  1258 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2);
       
  1259                          trees[ntree++] = tree;
       
  1260                          revs[tree]=-1;
       
  1261                          next}
       
  1262         /^rev:/   {rev=$2+0;
       
  1263                    if (revs[tree] == -1 || rev < revs[tree])
       
  1264                         { revs[tree] = rev; };
       
  1265                   next;}
       
  1266         END       {for (tree in trees)
       
  1267                         { rev=revs[trees[tree]];
       
  1268                           if (rev > 0)
       
  1269                                 {printf("%s %d\n",trees[tree],rev-1)}
       
  1270                         }}' | while read LINE
       
  1271     do
       
  1272         set - $LINE
       
  1273         TREE=$1
       
  1274         REV=$2
       
  1275         A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'`
       
  1276         FSTAT_OPT="--rev $A"
       
  1277         print "Revision: $A $REV" >> $FLIST
       
  1278         treestatus $TREE
       
  1279     done
       
  1280 }
       
  1281 
       
  1282 function flist_from_mercurial_forest
       
  1283 {
       
  1284     rm -f $FLIST
       
  1285     if [ -z "$Nflag" ]; then
       
  1286         print " File list from hg foutgoing $PWS ..."
       
  1287         outgoing_from_mercurial_forest
       
  1288         HG_LIST_FROM_COMMIT=1
       
  1289     fi
       
  1290     if [ ! -f $FLIST ]; then
       
  1291         # hg commit hasn't been run see what is lying around
       
  1292         print "\n No outgoing, perhaps you haven't commited."
       
  1293         print " File list from hg fstatus -mard ...\c"
       
  1294         FSTAT_OPT=
       
  1295         fstatus
       
  1296         HG_LIST_FROM_COMMIT=
       
  1297     fi
       
  1298     print " Done."
       
  1299 }
       
  1300 
       
  1301 #
       
  1302 # Used when dealing with the result of 'hg foutgoing'
       
  1303 # When now go down the tree and generate the change list
       
  1304 #
       
  1305 function treestatus
       
  1306 {
       
  1307     TREE=$1
       
  1308     HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
       
  1309 
       
  1310     $HGCMD -mdn 2>/dev/null | $FILTER | while read F
       
  1311     do
       
  1312         echo $TREE/$F
       
  1313     done >> $FLIST
       
  1314 
       
  1315     # Then all the added files
       
  1316     # But some of these could have been "moved" or renamed ones or copied ones
       
  1317     # so let's make sure we get the proper info
       
  1318     # hg status -aC will produce something like:
       
  1319     #	A subdir/File3
       
  1320     #	A subdir/File4
       
  1321     #	  File4
       
  1322     #	A subdir/File5
       
  1323     # The first and last are simple addition while the middle one
       
  1324     # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
       
  1325     # without also getting the status of removed files.  The middle case above
       
  1326     # is a rename if File4 is also shown a being removed.  If File4 is not a
       
  1327     # removed file, then the middle case is a copy from File4 to subdir/File4
       
  1328     # FIXME - we're not distinguishing copy from rename
       
  1329     $HGCMD -aC | $FILTER | while read LINE; do
       
  1330 	ldone=""
       
  1331 	while [ -z "$ldone" ]; do
       
  1332 	    ldone="1"
       
  1333 	    set - $LINE
       
  1334 	    if [ $# -eq 2 -a "$1" == "A" ]; then
       
  1335 		AFILE=$2
       
  1336 		if read LINE2; then
       
  1337 		    set - $LINE2
       
  1338 		    if [ $# -eq 1 ]; then
       
  1339 			echo $TREE/$AFILE $TREE/$1 >>$FLIST
       
  1340 		    elif [ $# -eq 2 ]; then
       
  1341 			echo $TREE/$AFILE >>$FLIST
       
  1342 			LINE=$LINE2
       
  1343 			ldone=""
       
  1344 		    fi
       
  1345 		else
       
  1346 		    echo $TREE/$AFILE >>$FLIST
       
  1347 		fi
       
  1348 	    fi
       
  1349 	done
       
  1350     done
       
  1351     $HGCMD -rn | $FILTER | while read RFILE; do
       
  1352 	grep "$TREE/$RFILE" $FLIST >/dev/null
       
  1353 	if [ $? -eq 1 ]; then
       
  1354 	    echo $TREE/$RFILE >>$FLIST
       
  1355 	fi
       
  1356     done
       
  1357 }
       
  1358 
       
  1359 function fstatus
       
  1360 {
       
  1361     #
       
  1362     # forest extension is still being changed. For instance the output
       
  1363     # of fstatus used to no prepend the tree path to filenames, but
       
  1364     # this has changed recently. AWK code below does try to handle both
       
  1365     # cases
       
  1366     #
       
  1367     hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
       
  1368 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
       
  1369 	$1 != ""	{n=index($1,tree);
       
  1370 			 if (n == 0)
       
  1371 				{ printf("%s/%s\n",tree,$1)}
       
  1372 			 else
       
  1373 				{ printf("%s\n",$1)}}' >> $FLIST
       
  1374 
       
  1375     #
       
  1376     # There is a bug in the output of fstatus -aC on recent versions: it
       
  1377     # inserts a space between the name of the tree and the filename of the
       
  1378     # old file. e.g.:
       
  1379     #
       
  1380     # $ hg fstatus -aC
       
  1381     # [.]
       
  1382     #
       
  1383     # [MyWS]
       
  1384     # A MyWS/subdir/File2
       
  1385     #  MyWS/ File2
       
  1386     #
       
  1387     # [MyWS2]
       
  1388     #
       
  1389 
       
  1390     hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
       
  1391 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
       
  1392 	/^A .*/		{n=index($2,tree);
       
  1393 			 if (n == 0)
       
  1394 				{ printf("A %s/%s\n",tree,$2)}
       
  1395 			 else
       
  1396 				{ printf("A %s\n",$2)};
       
  1397 			 next}
       
  1398 	/^ /		{n=index($1,tree);
       
  1399 			 if (n == 0)
       
  1400 				{ printf("%s/%s\n",tree,$1)}
       
  1401 			 else
       
  1402 				{ if (NF == 2)
       
  1403 					printf("%s/%s\n",tree,$2)
       
  1404 				  else
       
  1405 					printf("%s\n",$1)
       
  1406 				};
       
  1407 			 next}
       
  1408 	' | while read LINE; do
       
  1409 	ldone=""
       
  1410 	while [ -z "$ldone" ]; do
       
  1411 	    ldone="1"
       
  1412 	    set - $LINE
       
  1413 	    if [ $# -eq 2 -a "$1" == "A" ]; then
       
  1414 		AFILE=$2
       
  1415 		if read LINE2; then
       
  1416 		    set - $LINE2
       
  1417 		    if [ $# -eq 1 ]; then
       
  1418 			echo $AFILE $1 >>$FLIST
       
  1419 		    elif [ $# -eq 2 ]; then
       
  1420 			echo $AFILE >>$FLIST
       
  1421 			LINE=$LINE2
       
  1422 			ldone=""
       
  1423 		    fi
       
  1424 		else
       
  1425 		    echo $AFILE >>$FLIST
       
  1426 		fi
       
  1427 	    fi
       
  1428 	done
       
  1429     done
       
  1430     hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
       
  1431 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
       
  1432 	$1 != ""	{n=index($1,tree);
       
  1433 			 if (n == 0)
       
  1434 				{ printf("%s/%s\n",tree,$1)}
       
  1435 			 else
       
  1436 				{ printf("%s\n",$1)}}' | while read RFILE; do
       
  1437 	grep "$RFILE" $FLIST >/dev/null
       
  1438 	if [ $? -eq 1 ]; then
       
  1439 	    echo $RFILE >>$FLIST
       
  1440 	fi
       
  1441     done
       
  1442 }
       
  1443 
       
  1444 #
       
  1445 # flist_from_mercurial $PWS
       
  1446 #
       
  1447 # Only local file based repositories are supported at present
       
  1448 # since even though we can determine the list from the parent finding
       
  1449 # the changes is harder.
       
  1450 #
       
  1451 # We first look for any outgoing files, this is for when the user has
       
  1452 # run hg commit.  If we don't find any then we look with hg status.
       
  1453 #
       
  1454 # We need at least one of default-push or default paths set in .hg/hgrc
       
  1455 # If neither are set we don't know who to compare with.
       
  1456 
       
  1457 function flist_from_mercurial
       
  1458 {
       
  1459 #	if [ "${PWS##ssh://}" != "$PWS" -o \
       
  1460 #	     "${PWS##http://}" != "$PWS" -o \
       
  1461 #	     "${PWS##https://}" != "$PWS" ]; then
       
  1462 #		print "Remote Mercurial repositories not currently supported."
       
  1463 #		print "Set default and/or default-push to a local repository"
       
  1464 #		exit
       
  1465 #	fi
       
  1466     if [[ -n $forestflag ]]; then
       
  1467         HG_LIST_FROM_COMMIT=
       
  1468 	flist_from_mercurial_forest
       
  1469     else
       
  1470         STATUS_REV=
       
  1471         if [[ -n $rflag ]]; then
       
  1472             STATUS_REV="--rev $PARENT_REV"
       
  1473         elif [[ -n $OUTREV ]]; then
       
  1474             STATUS_REV="--rev $OUTREV"
       
  1475         else
       
  1476             # hg commit hasn't been run see what is lying around
       
  1477             print "\n No outgoing, perhaps you haven't commited."
       
  1478         fi
       
  1479 	# First let's list all the modified or deleted files
       
  1480 
       
  1481 	hg status $STATUS_REV -mdn | $FILTER > $FLIST
       
  1482 
       
  1483 	# Then all the added files
       
  1484 	# But some of these could have been "moved" or renamed ones
       
  1485 	# so let's make sure we get the proper info
       
  1486 	# hg status -aC will produce something like:
       
  1487 	#	A subdir/File3
       
  1488 	#	A subdir/File4
       
  1489 	#	  File4
       
  1490 	#	A subdir/File5
       
  1491         # The first and last are simple addition while the middle one
       
  1492         # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
       
  1493         # without also getting the status of removed files.  The middle case above
       
  1494         # is a rename if File4 is also shown a being removed.  If File4 is not a
       
  1495         # removed file, then the middle case is a copy from File4 to subdir/File4
       
  1496         # FIXME - we're not distinguishing copy from rename
       
  1497 
       
  1498 	hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
       
  1499 	while read LINE; do
       
  1500 	    ldone=""
       
  1501 	    while [ -z "$ldone" ]; do
       
  1502 		ldone="1"
       
  1503 		set - $LINE
       
  1504 		if [ $# -eq 2 -a "$1" == "A" ]; then
       
  1505 		    AFILE=$2
       
  1506 		    if read LINE2; then
       
  1507 			set - $LINE2
       
  1508 			if [ $# -eq 1 ]; then
       
  1509 			    echo $AFILE $1 >>$FLIST
       
  1510 			elif [ $# -eq 2 ]; then
       
  1511 			    echo $AFILE >>$FLIST
       
  1512 			    LINE=$LINE2
       
  1513 			    ldone=""
       
  1514 			fi
       
  1515 		    else
       
  1516 			echo $AFILE >>$FLIST
       
  1517 		    fi
       
  1518 		fi
       
  1519 	    done
       
  1520 	done < $FLIST.temp
       
  1521 	hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
       
  1522 	while read RFILE; do
       
  1523 	    grep "$RFILE" $FLIST >/dev/null
       
  1524 	    if [ $? -eq 1 ]; then
       
  1525 		echo $RFILE >>$FLIST
       
  1526 	    fi
       
  1527 	done < $FLIST.temp
       
  1528 	rm -f $FLIST.temp
       
  1529     fi
       
  1530 }
       
  1531 
       
  1532 function env_from_flist
       
  1533 {
       
  1534 	[[ -r $FLIST ]] || return
       
  1535 
       
  1536 	#
       
  1537 	# Use "eval" to set env variables that are listed in the file
       
  1538 	# list.  Then copy those into our local versions of those
       
  1539 	# variables if they have not been set already.
       
  1540 	#
       
  1541 	eval `sed -e "s/#.*$//" $FLIST | grep = `
       
  1542 
       
  1543 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
       
  1544 
       
  1545 	#
       
  1546 	# Check to see if CODEMGR_PARENT is set in the flist file.
       
  1547 	#
       
  1548 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
       
  1549 	    codemgr_parent=$CODEMGR_PARENT
       
  1550 }
       
  1551 
       
  1552 #
       
  1553 # detect_scm
       
  1554 #
       
  1555 # We dynamically test the SCM type; this allows future extensions to
       
  1556 # new SCM types
       
  1557 #
       
  1558 function detect_scm
       
  1559 {
       
  1560 	if hg root >/dev/null ; then
       
  1561 		print "mercurial"
       
  1562 	else
       
  1563 		print "unknown"
       
  1564 	fi
       
  1565 }
       
  1566 
       
  1567 function look_for_prog
       
  1568 {
       
  1569 	typeset path
       
  1570 	typeset ppath
       
  1571 	typeset progname=$1
       
  1572 
       
  1573 	DEVTOOLS=
       
  1574 	OS=`uname`
       
  1575 	if [[ "$OS" == "SunOS" ]]; then
       
  1576 	    DEVTOOLS="/java/devtools/`uname -p`/bin"
       
  1577 	elif [[ "$OS" == "Linux" ]]; then
       
  1578 	    DEVTOOLS="/java/devtools/linux/bin"
       
  1579 	fi
       
  1580 
       
  1581 	ppath=$PATH
       
  1582 	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
       
  1583 	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
       
  1584 	ppath=$ppath:/opt/onbld/bin/`uname -p`
       
  1585 	ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
       
  1586 
       
  1587 	PATH=$ppath prog=`whence $progname`
       
  1588 	if [[ -n $prog ]]; then
       
  1589 		print $prog
       
  1590 	fi
       
  1591 }
       
  1592 
       
  1593 #
       
  1594 # Find the parent for $1
       
  1595 #
       
  1596 function find_outrev
       
  1597 {
       
  1598     crev=$1
       
  1599     prev=`hg log -r $crev --template '{parents}\n'`
       
  1600     if [[ -z "$prev" ]]
       
  1601     then
       
  1602 	# No specific parent means previous changeset is parent
       
  1603 	prev=`expr $crev - 1`
       
  1604     else
       
  1605 	# Format is either of the following two:
       
  1606 	# 546:7df6fcf1183b
       
  1607 	# 548:16f1915bb5cd 547:ffaa4e775815
       
  1608 	prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
       
  1609     fi
       
  1610     print $prev
       
  1611 }
       
  1612 
       
  1613 function extract_ssh_infos
       
  1614 {
       
  1615     CMD=$1
       
  1616     if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
       
  1617 	ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
       
  1618 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
       
  1619 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
       
  1620     else
       
  1621 	ssh_user=
       
  1622 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
       
  1623 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
       
  1624     fi
       
  1625 
       
  1626 }
       
  1627 
       
  1628 function build_old_new_mercurial
       
  1629 {
       
  1630 	olddir=$1
       
  1631 	newdir=$2
       
  1632 	DIR=$3
       
  1633 	F=$4
       
  1634 	#
       
  1635 	# new version of the file.
       
  1636 	#
       
  1637 	rm -rf $newdir/$DIR/$F
       
  1638 	if [ -f $F ]; then
       
  1639 	    cp $F  $newdir/$DIR/$F
       
  1640 	fi
       
  1641 
       
  1642 	#
       
  1643 	# Old version of the file.
       
  1644 	#
       
  1645 	rm -rf $olddir/$DIR/$F
       
  1646 
       
  1647 	if [ -n "$PWS" ]; then
       
  1648 	    if expr "$PWS" : 'ssh://' >/dev/null
       
  1649 	    then
       
  1650 		extract_ssh_infos $PWS
       
  1651 		if [ -n "$ssh_user" ]; then
       
  1652 		    parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
       
  1653 		else
       
  1654 		    parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
       
  1655 		fi
       
  1656 	    else
       
  1657 		parent="hg -R $PWS --cwd $PWS"
       
  1658 	    fi
       
  1659 	else
       
  1660 	    parent=""
       
  1661 	fi
       
  1662 
       
  1663 	if [ -z "$rename" ]; then
       
  1664 	    if [ -n "$rflag" ]; then
       
  1665 		parentrev=$PARENT_REV
       
  1666 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
       
  1667                 parentrev=$OUTREV
       
  1668 	    else
       
  1669                 if [[ -n $HG_BRANCH ]]; then
       
  1670                     parentrev=$HG_BRANCH
       
  1671                 else
       
  1672 		    parentrev="tip"
       
  1673                 fi
       
  1674 	    fi
       
  1675 
       
  1676 	    if [ -n "$parentrev" ]; then
       
  1677 		if [ -z "$parent" ]; then
       
  1678 		    hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
       
  1679 		else
       
  1680 		    # when specifying a workspace we have to provide
       
  1681 		    # the full path
       
  1682 		    $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
       
  1683 		fi
       
  1684 	    fi
       
  1685 	else
       
  1686 	    # It's a rename (or a move), or a copy, so let's make sure we move
       
  1687 	    # to the right directory first, then restore it once done
       
  1688 	    current_dir=`pwd`
       
  1689 	    hg_root=`hg root`
       
  1690 	    cd $CWS
       
  1691 	    if [ -n "$rflag" ]; then
       
  1692 		parentrev=$PARENT_REV
       
  1693 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
       
  1694                 parentrev=$OUTREV
       
  1695 	    fi
       
  1696 	    if [ -z "$parentrev" ]; then
       
  1697 		parentrev=`hg log -l1 $PDIR/$PF | $AWK -F: '/changeset/ {print $2}'`
       
  1698 	    fi
       
  1699 	    if [ -n "$parentrev" ]; then
       
  1700 		mkdir -p $olddir/$PDIR
       
  1701 		if [ -z "$parent" ]; then
       
  1702 		    hg cat -R $hg_root --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
       
  1703 		else
       
  1704 		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
       
  1705 		fi
       
  1706 	    fi
       
  1707 	    cd $current_dir
       
  1708 	fi
       
  1709 }
       
  1710 
       
  1711 function build_old_new
       
  1712 {
       
  1713 	if [[ $SCM_MODE == "mercurial" ]]; then
       
  1714 		build_old_new_mercurial $@
       
  1715 	fi
       
  1716 }
       
  1717 
       
  1718 
       
  1719 #
       
  1720 # Usage message.
       
  1721 #
       
  1722 function usage
       
  1723 {
       
  1724 	print "Usage:\twebrev [options]
       
  1725 	webrev [options] ( <file> | - )
       
  1726 
       
  1727 Options:
       
  1728 	-v: Print the version of this tool.
       
  1729         -b: Do not ignore changes in the amount of white space.
       
  1730         -c <CR#>: Include link to CR (aka bugid) in the main page.
       
  1731 	-i <filename>: Include <filename> in the index.html file.
       
  1732 	-o <outdir>: Output webrev to specified directory.
       
  1733 	-p <compare-against>: Use specified parent wkspc or basis for comparison
       
  1734         -u <username>: Use that username instead of 'guessing' one.
       
  1735 	-m: Forces the use of Mercurial
       
  1736 
       
  1737 Mercurial only options:
       
  1738 	-r rev: Compare against a specified revision
       
  1739 	-N: Skip 'hg outgoing', use only 'hg status'
       
  1740 	-f: Use the forest extension
       
  1741 
       
  1742 Arguments:
       
  1743 	<file>: Optional file containing list of files to include in webrev
       
  1744         -: read list of files to include in webrev from standard input
       
  1745 
       
  1746 Environment:
       
  1747 	WDIR: Control the output directory.
       
  1748 	WEBREV_BUGURL: Control the URL prefix for bugids.
       
  1749 
       
  1750 "
       
  1751 
       
  1752 	exit 2
       
  1753 }
       
  1754 
       
  1755 #
       
  1756 #
       
  1757 # Main program starts here
       
  1758 #
       
  1759 #
       
  1760 LANG="C"
       
  1761 LC_ALL="C"
       
  1762 export LANG LC_ALL
       
  1763 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
       
  1764 
       
  1765 set +o noclobber
       
  1766 
       
  1767 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
       
  1768 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
       
  1769 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
       
  1770 [[ -z $PERL ]] && PERL=`look_for_prog perl`
       
  1771 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
       
  1772 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
       
  1773 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
       
  1774 [[ -z $AWK ]] && AWK=`look_for_prog awk`
       
  1775 [[ -z $JAR ]] && JAR=`look_for_prog jar`
       
  1776 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
       
  1777 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
       
  1778 [[ -z $WGET ]] && WGET=`look_for_prog wget`
       
  1779 
       
  1780 if uname | grep CYGWIN >/dev/null
       
  1781 then
       
  1782         ISWIN=1
       
  1783         # Under windows mercurial outputs '\' instead of '/'
       
  1784         FILTER="tr '\\\\' '/'"
       
  1785 else
       
  1786         FILTER="cat"
       
  1787 fi
       
  1788 
       
  1789 if [[ ! -x $PERL ]]; then
       
  1790 	print -u2 "Error: No perl interpreter found.  Exiting."
       
  1791 	exit 1
       
  1792 fi
       
  1793 
       
  1794 #
       
  1795 # These aren't fatal, but we want to note them to the user.
       
  1796 #
       
  1797 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
       
  1798 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
       
  1799 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
       
  1800 
       
  1801 # Declare global total counters.
       
  1802 integer TOTL TINS TDEL TMOD TUNC
       
  1803 
       
  1804 flist_mode=
       
  1805 flist_file=
       
  1806 bflag=
       
  1807 iflag=
       
  1808 oflag=
       
  1809 pflag=
       
  1810 uflag=
       
  1811 Oflag=
       
  1812 rflag=
       
  1813 Nflag=
       
  1814 forestflag=
       
  1815 while getopts "c:i:o:p:r:u:mONvfb" opt
       
  1816 do
       
  1817 	case $opt in
       
  1818         b)      bflag=1;;
       
  1819 
       
  1820 	i)	iflag=1
       
  1821 		INCLUDE_FILE=$OPTARG;;
       
  1822 
       
  1823 	o)	oflag=1
       
  1824 		WDIR=$OPTARG;;
       
  1825 
       
  1826 	p)	pflag=1
       
  1827 		codemgr_parent=$OPTARG;;
       
  1828 
       
  1829 	u)      uflag=1
       
  1830 		username=$OPTARG;;
       
  1831 
       
  1832         c)      if [[ -z $CRID ]]; then
       
  1833                    CRID=$OPTARG
       
  1834                 else
       
  1835                    CRID="$CRID $OPTARG"
       
  1836                 fi;;
       
  1837 
       
  1838 	m)	SCM_MODE="mercurial";;
       
  1839 
       
  1840 	O)	Oflag=1;; # ignored (bugs are now all visible at bugs.openjdk.java.net)
       
  1841 
       
  1842 	N)	Nflag=1;;
       
  1843 
       
  1844 	f)	forestflag=1;;
       
  1845 
       
  1846 	r)	rflag=1
       
  1847 		PARENT_REV=$OPTARG;;
       
  1848 
       
  1849 	v)	print "$0 version: $WEBREV_UPDATED";;
       
  1850 
       
  1851 
       
  1852 	?)	usage;;
       
  1853 	esac
       
  1854 done
       
  1855 
       
  1856 FLIST=/tmp/$$.flist
       
  1857 HG_LIST_FROM_COMMIT=
       
  1858 
       
  1859 if [[ -n $forestflag && -n $rflag ]]; then
       
  1860     print "The -r <rev> flag is incompatible with the use of forests"
       
  1861     exit 2
       
  1862 fi
       
  1863 
       
  1864 #
       
  1865 # If this manually set as the parent, and it appears to be an earlier webrev,
       
  1866 # then note that fact and set the parent to the raw_files/new subdirectory.
       
  1867 #
       
  1868 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
       
  1869 	parent_webrev="$codemgr_parent"
       
  1870 	codemgr_parent="$codemgr_parent/raw_files/new"
       
  1871 fi
       
  1872 
       
  1873 shift $(($OPTIND - 1))
       
  1874 
       
  1875 if [[ $1 == "-" ]]; then
       
  1876 	cat > $FLIST
       
  1877 	flist_mode="stdin"
       
  1878 	flist_done=1
       
  1879 	shift
       
  1880 elif [[ -n $1 ]]; then
       
  1881 	if [[ ! -r $1 ]]; then
       
  1882 		print -u2 "$1: no such file or not readable"
       
  1883 		usage
       
  1884 	fi
       
  1885 	cat $1 > $FLIST
       
  1886 	flist_mode="file"
       
  1887 	flist_file=$1
       
  1888 	flist_done=1
       
  1889 	shift
       
  1890 else
       
  1891 	flist_mode="auto"
       
  1892 fi
       
  1893 
       
  1894 #
       
  1895 # Before we go on to further consider -l and -w, work out which SCM we think
       
  1896 # is in use.
       
  1897 #
       
  1898 if [[ -z $SCM_MODE ]]; then
       
  1899     SCM_MODE=`detect_scm $FLIST`
       
  1900 fi
       
  1901 if [[ $SCM_MODE == "unknown" ]]; then
       
  1902        print -u2 "Unable to determine SCM type currently in use."
       
  1903        print -u2 "For mercurial: webrev runs 'hg root'."
       
  1904        exit 1
       
  1905 fi
       
  1906 
       
  1907 print -u2 "   SCM detected: $SCM_MODE"
       
  1908 
       
  1909 
       
  1910 if [[ $SCM_MODE == "mercurial" ]]; then
       
  1911     #
       
  1912     # determine Workspace and parent workspace paths
       
  1913     #
       
  1914     CWS=`hg root | $FILTER`
       
  1915     if [[ -n $pflag && -z "$PWS" ]]; then
       
  1916 	OUTPWS=$codemgr_parent
       
  1917         # Let's try to expand it if it's an alias defined in [paths]
       
  1918         tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
       
  1919         if [[ -n $tmp ]]; then
       
  1920             OUTPWS="$tmp"
       
  1921         fi
       
  1922         if [[ -n $rflag ]]; then
       
  1923 	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
       
  1924 	        PWS=$codemgr_parent
       
  1925 	    else
       
  1926 	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
       
  1927 	    fi
       
  1928         fi
       
  1929     fi
       
  1930     #
       
  1931     # OUTPWS is the parent repository to use when using 'hg outgoing'
       
  1932     #
       
  1933     if [[ -z $Nflag ]]; then
       
  1934         if [[ -n $forestflag ]]; then
       
  1935             #
       
  1936             # for forest we have to rely on properly set default and
       
  1937             # default-push because they can be different from the top one.
       
  1938             # unless of course it was explicitly specified with -p
       
  1939             if [[ -z $pflag ]]; then
       
  1940                 OUTPWS=
       
  1941             fi
       
  1942         else
       
  1943             #
       
  1944             # Unfortunately mercurial is bugged and doesn't handle
       
  1945             # aliases correctly in 'hg path default'
       
  1946             # So let's do it ourselves. Sigh...
       
  1947             if [[ -z "$OUTPWS" ]]; then
       
  1948                 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
       
  1949             fi
       
  1950             # Still empty, means no default-push
       
  1951             if [[ -z "$OUTPWS" ]]; then
       
  1952                 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
       
  1953             fi
       
  1954             # Let's try to expand it if it's an alias defined in [paths]
       
  1955             tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
       
  1956             if [[ -n $tmp ]]; then
       
  1957                 OUTPWS="$tmp"
       
  1958             fi
       
  1959         fi
       
  1960     fi
       
  1961     #
       
  1962     # OUTPWS may contain username:password, let's make sure we remove the
       
  1963     # sensitive information before we print out anything in the HTML
       
  1964     #
       
  1965     OUTPWS2=$OUTPWS
       
  1966     if [[ -n $OUTPWS ]]; then
       
  1967 	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
       
  1968 	    # Remove everything between '://' and '@'
       
  1969 	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
       
  1970 	fi
       
  1971     fi
       
  1972 
       
  1973     if [[ -z $HG_BRANCH ]]; then
       
  1974         HG_BRANCH=`hg branch`
       
  1975         if [ "$HG_BRANCH" == "default" ]; then
       
  1976             #
       
  1977             # 'default' means no particular branch, so let's cancel that
       
  1978             #
       
  1979             HG_BRANCH=
       
  1980         fi
       
  1981     fi
       
  1982 
       
  1983     if [[ -z $forestflag ]]; then
       
  1984         if [[ -z $Nflag ]]; then
       
  1985             #
       
  1986             # If no "-N", always do "hg outgoing" against parent
       
  1987             # repository to determine list of outgoing revisions.
       
  1988             #
       
  1989             ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
       
  1990             if [[ -n $ALL_CREV ]]; then
       
  1991                 FIRST_CREV=`echo "$ALL_CREV" | head -1`
       
  1992                 #
       
  1993                 # If no "-r", choose revision to compare against by
       
  1994                 # finding the latest revision not in the outgoing list.
       
  1995                 #
       
  1996                 if [[ -z $rflag ]]; then
       
  1997                     OUTREV=`find_outrev "$FIRST_CREV"`
       
  1998                     if [[ -n $OUTREV ]]; then
       
  1999                         HG_LIST_FROM_COMMIT=1
       
  2000                     fi
       
  2001                 fi
       
  2002             fi
       
  2003         elif [[ -n $rflag ]]; then
       
  2004             #
       
  2005             # If skipping "hg outgoing" but still comparing against a
       
  2006             # specific revision (not the tip), set revision for comment
       
  2007             # accumulation.
       
  2008             #
       
  2009             FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
       
  2010             FIRST_CREV=`expr $FIRST_CREV + 1`
       
  2011         fi
       
  2012     fi
       
  2013     #Let's check if a merge is needed, if so, issue a warning
       
  2014     PREV=`hg parent | grep '^tag:.*tip$'`
       
  2015     if [[ -z $PREV ]]; then
       
  2016         print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
       
  2017     fi
       
  2018 fi
       
  2019 
       
  2020 if [[ $flist_mode == "stdin" ]]; then
       
  2021 	print -u2 " File list from: standard input"
       
  2022 elif [[ $flist_mode == "file" ]]; then
       
  2023 	print -u2 " File list from: $flist_file"
       
  2024 fi
       
  2025 
       
  2026 if [[ $# -gt 0 ]]; then
       
  2027 	print -u2 "WARNING: unused arguments: $*"
       
  2028 fi
       
  2029 
       
  2030 if [[ $SCM_MODE == "mercurial" ]]; then
       
  2031     if [[ -z $flist_done ]]; then
       
  2032 	flist_from_mercurial $PWS
       
  2033     fi
       
  2034 fi
       
  2035 
       
  2036 #
       
  2037 # If the user didn't specify a -i option, check to see if there is a
       
  2038 # webrev-info file in the workspace directory.
       
  2039 #
       
  2040 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
       
  2041 	iflag=1
       
  2042 	INCLUDE_FILE="$CWS/webrev-info"
       
  2043 fi
       
  2044 
       
  2045 if [[ -n $iflag ]]; then
       
  2046 	if [[ ! -r $INCLUDE_FILE ]]; then
       
  2047 		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
       
  2048 		    "not readable."
       
  2049 		exit 1
       
  2050 	else
       
  2051 		#
       
  2052 		# $INCLUDE_FILE may be a relative path, and the script alters
       
  2053 		# PWD, so we just stash a copy in /tmp.
       
  2054 		#
       
  2055 		cp $INCLUDE_FILE /tmp/$$.include
       
  2056 	fi
       
  2057 fi
       
  2058 
       
  2059 #
       
  2060 # Output directory.
       
  2061 #
       
  2062 if [[ -z $WDIR ]]; then
       
  2063     WDIR=$CWS/webrev
       
  2064 else
       
  2065     # If the output directory doesn't end with '/webrev' or '/webrev/'
       
  2066     # then add '/webrev'. This is for backward compatibility
       
  2067     if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
       
  2068     then
       
  2069 	WDIR=$WDIR/webrev
       
  2070     fi
       
  2071 fi
       
  2072 # WDIR=${WDIR:-$CWS/webrev}
       
  2073 
       
  2074 #
       
  2075 # Name of the webrev, derived from the workspace name; in the
       
  2076 # future this could potentially be an option.
       
  2077 #
       
  2078 # Let's keep what's after the last '/'
       
  2079 WNAME=${CWS##*/}
       
  2080 
       
  2081 #
       
  2082 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
       
  2083 #
       
  2084 if [ ${WDIR%%/*} ]; then
       
  2085     if [[ -n $ISWIN ]]; then
       
  2086         if [ ${WDIR%%[A-Za-z]:*} ]; then
       
  2087 	    WDIR=$PWD/$WDIR
       
  2088         fi
       
  2089     else
       
  2090 	WDIR=$PWD/$WDIR
       
  2091     fi
       
  2092 fi
       
  2093 
       
  2094 if [[ ! -d $WDIR ]]; then
       
  2095 	mkdir -p $WDIR
       
  2096 	[[ $? != 0 ]] && exit 1
       
  2097 fi
       
  2098 
       
  2099 #
       
  2100 # Summarize what we're going to do.
       
  2101 #
       
  2102 print "      Workspace: $CWS"
       
  2103 if [[ -n $parent_webrev ]]; then
       
  2104     print "Compare against: webrev at $parent_webrev"
       
  2105 elif [[ -n $OUTPWS2 ]]; then
       
  2106     print "Compare against: $OUTPWS2"
       
  2107 fi
       
  2108 if [[ -n $HG_BRANCH ]]; then
       
  2109     print "         Branch: $HG_BRANCH"
       
  2110 fi
       
  2111 if [[ -n $rflag ]]; then
       
  2112         print "Compare against version: $PARENT_REV"
       
  2113 fi
       
  2114 [[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
       
  2115 print "      Output to: $WDIR"
       
  2116 
       
  2117 #
       
  2118 # Save the file list in the webrev dir
       
  2119 #
       
  2120 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
       
  2121 
       
  2122 #
       
  2123 #    Bug IDs will be replaced by a URL.  Order of precedence
       
  2124 #    is: default location, $WEBREV_BUGURL
       
  2125 #
       
  2126 BUGURL='https://bugs.openjdk.java.net/browse/'
       
  2127 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
       
  2128 IDPREFIX='JDK-'
       
  2129 
       
  2130 
       
  2131 rm -f $WDIR/$WNAME.patch
       
  2132 rm -f $WDIR/$WNAME.changeset
       
  2133 rm -f $WDIR/$WNAME.ps
       
  2134 rm -f $WDIR/$WNAME.pdf
       
  2135 
       
  2136 touch $WDIR/$WNAME.patch
       
  2137 
       
  2138 print "   Output Files:"
       
  2139 
       
  2140 #
       
  2141 # Clean up the file list: Remove comments, blank lines and env variables.
       
  2142 #
       
  2143 sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
       
  2144 FLIST=/tmp/$$.flist.clean
       
  2145 
       
  2146 #
       
  2147 # Clean up residual raw files
       
  2148 #
       
  2149 if [ -d $WDIR/raw_files ]; then
       
  2150     rm -rf $WDIR/raw_files 2>/dev/null
       
  2151 fi
       
  2152 
       
  2153 #
       
  2154 # Should we ignore changes in white spaces when generating diffs?
       
  2155 #
       
  2156 if [[ -n $bflag ]]; then
       
  2157     DIFFOPTS="-t"
       
  2158 else
       
  2159     DIFFOPTS="-bt"
       
  2160 fi
       
  2161 #
       
  2162 # First pass through the files: generate the per-file webrev HTML-files.
       
  2163 #
       
  2164 while read LINE
       
  2165 do
       
  2166 	set - $LINE
       
  2167 	P=$1
       
  2168 
       
  2169         if [[ $1 == "Revision:" ]]; then
       
  2170             OUTREV=$2
       
  2171             continue
       
  2172         fi
       
  2173 	#
       
  2174 	# Normally, each line in the file list is just a pathname of a
       
  2175 	# file that has been modified or created in the child.  A file
       
  2176 	# that is renamed in the child workspace has two names on the
       
  2177 	# line: new name followed by the old name.
       
  2178 	#
       
  2179 	oldname=""
       
  2180 	oldpath=""
       
  2181 	rename=
       
  2182 	if [[ $# -eq 2 ]]; then
       
  2183 		PP=$2			# old filename
       
  2184 		oldname=" (was $PP)"
       
  2185 		oldpath="$PP"
       
  2186 		rename=1
       
  2187         	PDIR=${PP%/*}
       
  2188         	if [[ $PDIR == $PP ]]; then
       
  2189 			PDIR="."   # File at root of workspace
       
  2190 		fi
       
  2191 
       
  2192 		PF=${PP##*/}
       
  2193 
       
  2194 	        DIR=${P%/*}
       
  2195 	        if [[ $DIR == $P ]]; then
       
  2196 			DIR="."   # File at root of workspace
       
  2197 		fi
       
  2198 
       
  2199 		F=${P##*/}
       
  2200         else
       
  2201 	        DIR=${P%/*}
       
  2202 	        if [[ "$DIR" == "$P" ]]; then
       
  2203 			DIR="."   # File at root of workspace
       
  2204 		fi
       
  2205 
       
  2206 		F=${P##*/}
       
  2207 
       
  2208 		PP=$P
       
  2209 		PDIR=$DIR
       
  2210 		PF=$F
       
  2211 	fi
       
  2212 
       
  2213         # Make the webrev directory if necessary as it may have been
       
  2214         # removed because it was empty
       
  2215         if [ ! -d $CWS/$DIR ]; then
       
  2216 	    mkdir -p $CWS/$DIR
       
  2217         fi
       
  2218 
       
  2219 	COMM=`getcomments html $P $PP`
       
  2220 
       
  2221 	print "\t$P$oldname\n\t\t\c"
       
  2222 
       
  2223 	# Make the webrev mirror directory if necessary
       
  2224 	mkdir -p $WDIR/$DIR
       
  2225 
       
  2226 	# cd to the directory so the names are short
       
  2227 	cd $CWS/$DIR
       
  2228 
       
  2229 	#
       
  2230 	# We stash old and new files into parallel directories in /tmp
       
  2231 	# and do our diffs there.  This makes it possible to generate
       
  2232 	# clean looking diffs which don't have absolute paths present.
       
  2233 	#
       
  2234 	olddir=$WDIR/raw_files/old
       
  2235 	newdir=$WDIR/raw_files/new
       
  2236 	mkdir -p $olddir
       
  2237 	mkdir -p $newdir
       
  2238 	mkdir -p $olddir/$PDIR
       
  2239 	mkdir -p $newdir/$DIR
       
  2240 
       
  2241 	build_old_new $olddir $newdir $DIR $F
       
  2242 
       
  2243 	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
       
  2244 		print "*** Error: file not in parent or child"
       
  2245 		continue
       
  2246 	fi
       
  2247 
       
  2248 	cd $WDIR/raw_files
       
  2249 	ofile=old/$PDIR/$PF
       
  2250 	nfile=new/$DIR/$F
       
  2251 
       
  2252 	mv_but_nodiff=
       
  2253 	cmp $ofile $nfile > /dev/null 2>&1
       
  2254 	if [[ $? == 0 && $rename == 1 ]]; then
       
  2255 		mv_but_nodiff=1
       
  2256 	fi
       
  2257 
       
  2258         #
       
  2259         # Cleaning up
       
  2260         #
       
  2261         rm -f $WDIR/$DIR/$F.cdiff.html
       
  2262         rm -f $WDIR/$DIR/$F.udiff.html
       
  2263         rm -f $WDIR/$DIR/$F.wdiff.html
       
  2264         rm -f $WDIR/$DIR/$F.sdiff.html
       
  2265         rm -f $WDIR/$DIR/$F-.html
       
  2266         rm -f $WDIR/$DIR/$F.html
       
  2267 
       
  2268 	its_a_jar=
       
  2269 	if expr $F : '.*\.jar' \| $F : '.*\.zip' >/dev/null; then
       
  2270 	    its_a_jar=1
       
  2271 	    # It's a JAR or ZIP file, let's do it differently
       
  2272 	    if [[ -z $JAR ]]; then
       
  2273 		print "No access to jar, so can't produce diffs for jar or zip files"
       
  2274 	    else
       
  2275 		if [ -f $ofile ]; then
       
  2276 		    $JAR -tvf $ofile >"$ofile".lst
       
  2277 		fi
       
  2278 		if [ -f $nfile ]; then
       
  2279 		    $JAR -tvf $nfile >"$nfile".lst
       
  2280 		fi
       
  2281 
       
  2282 		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
       
  2283 
       
  2284 		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
       
  2285 		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
       
  2286 			> $WDIR/$DIR/$F.cdiff.html
       
  2287 		    print " cdiffs\c"
       
  2288 
       
  2289 		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
       
  2290 		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
       
  2291 			> $WDIR/$DIR/$F.udiff.html
       
  2292 
       
  2293 		    print " udiffs\c"
       
  2294 
       
  2295 		    if [[ -x $WDIFF ]]; then
       
  2296 			$WDIFF -c "$COMM" \
       
  2297 			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
       
  2298 			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
       
  2299 			if [[ $? -eq 0 ]]; then
       
  2300 			    print " wdiffs\c"
       
  2301 			else
       
  2302 			    print " wdiffs[fail]\c"
       
  2303 			fi
       
  2304 		    fi
       
  2305 
       
  2306 		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
       
  2307 			> $WDIR/$DIR/$F.sdiff.html
       
  2308 		    print " sdiffs\c"
       
  2309 
       
  2310 		    print " frames\c"
       
  2311 
       
  2312 		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
       
  2313 
       
  2314 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
       
  2315 
       
  2316 		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
       
  2317 		# renamed file: may also have differences
       
  2318 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
       
  2319 		elif [[ -f $nfile ]]; then
       
  2320 		# new file: count added lines
       
  2321 		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
       
  2322 		elif [[ -f $ofile ]]; then
       
  2323 		# old file: count deleted lines
       
  2324 		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
       
  2325 		fi
       
  2326 	    fi
       
  2327 	else
       
  2328 
       
  2329 	    #
       
  2330 	    # If we have old and new versions of the file then run the
       
  2331 	    # appropriate diffs.  This is complicated by a couple of factors:
       
  2332 	    #
       
  2333 	    #	- renames must be handled specially: we emit a 'remove'
       
  2334 	    #	  diff and an 'add' diff
       
  2335 	    #	- new files and deleted files must be handled specially
       
  2336 	    #	- Solaris patch(1m) can't cope with file creation
       
  2337 	    #	  (and hence renames) as of this writing.
       
  2338 	    #   - To make matters worse, gnu patch doesn't interpret the
       
  2339 	    #	  output of Solaris diff properly when it comes to
       
  2340 	    #	  adds and deletes.  We need to do some "cleansing"
       
  2341 	    #     transformations:
       
  2342 	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
       
  2343 	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
       
  2344 	    #
       
  2345 	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
       
  2346 	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
       
  2347 
       
  2348             if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]];
       
  2349             then
       
  2350               # Only need to generate a patch file here if there are no commits in outgoing
       
  2351               # or if we've specified a file list
       
  2352               rm -f $WDIR/$DIR/$F.patch
       
  2353               if [[ -z $rename ]]; then
       
  2354                   if [ ! -f $ofile ]; then
       
  2355                       diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
       
  2356                           > $WDIR/$DIR/$F.patch
       
  2357                   elif [ ! -f $nfile ]; then
       
  2358                       diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
       
  2359                           > $WDIR/$DIR/$F.patch
       
  2360                   else
       
  2361                       diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
       
  2362                   fi
       
  2363               else
       
  2364                   diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
       
  2365                       > $WDIR/$DIR/$F.patch
       
  2366 
       
  2367                   diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
       
  2368                       >> $WDIR/$DIR/$F.patch
       
  2369 
       
  2370               fi
       
  2371 
       
  2372 
       
  2373             #
       
  2374             # Tack the patch we just made onto the accumulated patch for the
       
  2375             # whole wad.
       
  2376             #
       
  2377               cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
       
  2378             fi
       
  2379 
       
  2380             print " patch\c"
       
  2381 
       
  2382 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
       
  2383 
       
  2384 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
       
  2385 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
       
  2386 		    > $WDIR/$DIR/$F.cdiff.html
       
  2387 		print " cdiffs\c"
       
  2388 
       
  2389 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
       
  2390 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
       
  2391 		    > $WDIR/$DIR/$F.udiff.html
       
  2392 
       
  2393 		print " udiffs\c"
       
  2394 
       
  2395 		if [[ -x $WDIFF ]]; then
       
  2396 		    $WDIFF -c "$COMM" \
       
  2397 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
       
  2398 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
       
  2399 		    if [[ $? -eq 0 ]]; then
       
  2400 			print " wdiffs\c"
       
  2401 		    else
       
  2402 			print " wdiffs[fail]\c"
       
  2403 		    fi
       
  2404 		fi
       
  2405 
       
  2406 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
       
  2407 		    > $WDIR/$DIR/$F.sdiff.html
       
  2408 		print " sdiffs\c"
       
  2409 
       
  2410 		print " frames\c"
       
  2411 
       
  2412 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
       
  2413 
       
  2414 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
       
  2415 
       
  2416 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
       
  2417 		# renamed file: may also have differences
       
  2418 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
       
  2419 	    elif [[ -f $nfile ]]; then
       
  2420 		# new file: count added lines
       
  2421 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
       
  2422 	    elif [[ -f $ofile ]]; then
       
  2423 		# old file: count deleted lines
       
  2424 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
       
  2425 	    fi
       
  2426 	fi
       
  2427 	#
       
  2428 	# Now we generate the postscript for this file.  We generate diffs
       
  2429 	# only in the event that there is delta, or the file is new (it seems
       
  2430 	# tree-killing to print out the contents of deleted files).
       
  2431 	#
       
  2432 	if [[ -f $nfile ]]; then
       
  2433 		ocr=$ofile
       
  2434 		[[ ! -f $ofile ]] && ocr=/dev/null
       
  2435 
       
  2436 		if [[ -z $mv_but_nodiff ]]; then
       
  2437 			textcomm=`getcomments text $P $PP`
       
  2438 			if [[ -x $CODEREVIEW ]]; then
       
  2439 				$CODEREVIEW -y "$textcomm" \
       
  2440 				    -e $ocr $nfile \
       
  2441 				    > /tmp/$$.psfile 2>/dev/null &&
       
  2442 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
       
  2443 				if [[ $? -eq 0 ]]; then
       
  2444 					print " ps\c"
       
  2445 				else
       
  2446 					print " ps[fail]\c"
       
  2447 				fi
       
  2448 			fi
       
  2449 		fi
       
  2450 	fi
       
  2451 
       
  2452 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
       
  2453 	    if [[ -n $its_a_jar ]]; then
       
  2454 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
       
  2455 	    else
       
  2456 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
       
  2457 	    fi
       
  2458 		print " old\c"
       
  2459 	fi
       
  2460 
       
  2461 	if [[ -f $nfile ]]; then
       
  2462 	    if [[ -n $its_a_jar ]]; then
       
  2463 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
       
  2464 	    else
       
  2465 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
       
  2466 	    fi
       
  2467 		print " new\c"
       
  2468 	fi
       
  2469 
       
  2470 	print
       
  2471 done < $FLIST
       
  2472 
       
  2473 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
       
  2474 if [[ $SCM_MODE == "mercurial" ]]; then
       
  2475   if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then
       
  2476     EXPORTCHANGESET="$WNAME.changeset"
       
  2477     CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
       
  2478     rm -f $CHANGESETPATH
       
  2479     touch $CHANGESETPATH
       
  2480     if [[ -n $ALL_CREV ]]; then
       
  2481       rev_opt=
       
  2482       for rev in $ALL_CREV; do
       
  2483         rev_opt="$rev_opt --rev $rev"
       
  2484       done
       
  2485     elif [[ -n $FIRST_CREV ]]; then
       
  2486       rev_opt="--rev $FIRST_CREV"
       
  2487     fi
       
  2488 
       
  2489     if [[ -n $rev_opt ]]; then
       
  2490       (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
       
  2491       echo "Created changeset: $CHANGESETPATH" 1>&2
       
  2492       # Use it in place of the jdk.patch created above
       
  2493       rm -f $WDIR/$WNAME.patch
       
  2494     fi
       
  2495   set +x
       
  2496   fi
       
  2497 fi
       
  2498 
       
  2499 frame_nav_js > $WDIR/ancnav.js
       
  2500 frame_navigation > $WDIR/ancnav.html
       
  2501 
       
  2502 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
       
  2503 	print " Generating PDF: \c"
       
  2504 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
       
  2505 	print "Done."
       
  2506 fi
       
  2507 
       
  2508 # Now build the index.html file that contains
       
  2509 # links to the source files and their diffs.
       
  2510 
       
  2511 cd $CWS
       
  2512 
       
  2513 # Save total changed lines for Code Inspection.
       
  2514 print "$TOTL" > $WDIR/TotalChangedLines
       
  2515 
       
  2516 print "     index.html: \c"
       
  2517 INDEXFILE=$WDIR/index.html
       
  2518 exec 3<&1			# duplicate stdout to FD3.
       
  2519 exec 1<&-			# Close stdout.
       
  2520 exec > $INDEXFILE		# Open stdout to index file.
       
  2521 
       
  2522 print "$HTML<head>"
       
  2523 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
       
  2524 print "$STDHEAD"
       
  2525 print "<title>$WNAME</title>"
       
  2526 print "</head>"
       
  2527 print "<body id=\"SUNWwebrev\">"
       
  2528 print "<div class=\"summary\">"
       
  2529 print "<h2>Code Review for $WNAME</h2>"
       
  2530 
       
  2531 print "<table>"
       
  2532 
       
  2533 if [[ -z $uflag ]]; then
       
  2534     if [[ $SCM_MODE == "mercurial" ]]; then
       
  2535         #
       
  2536         # Let's try to extract the user name from the .hgrc file
       
  2537         #
       
  2538 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
       
  2539     fi
       
  2540 
       
  2541     if [[ -z $username ]]; then
       
  2542         #
       
  2543         # Figure out the username and gcos name.  To maintain compatibility
       
  2544         # with passwd(4), we must support '&' substitutions.
       
  2545         #
       
  2546 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
       
  2547 	if [[ -x $GETENT ]]; then
       
  2548 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
       
  2549 	fi
       
  2550 	userupper=`print "$username" | sed 's/\<./\u&/g'`
       
  2551 	realname=`print $realname | sed s/\&/$userupper/`
       
  2552     fi
       
  2553 fi
       
  2554 
       
  2555 date="on `date`"
       
  2556 
       
  2557 if [[ -n "$username" && -n "$realname" ]]; then
       
  2558 	print "<tr><th>Prepared by:</th>"
       
  2559 	print "<td>$realname ($username) $date</td></tr>"
       
  2560 elif [[ -n "$username" ]]; then
       
  2561 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
       
  2562 fi
       
  2563 
       
  2564 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
       
  2565 if [[ -n $parent_webrev ]]; then
       
  2566         print "<tr><th>Compare against:</th><td>"
       
  2567 	print "webrev at $parent_webrev"
       
  2568 else
       
  2569     if [[ -n $OUTPWS2 ]]; then
       
  2570         print "<tr><th>Compare against:</th><td>"
       
  2571 	print "$OUTPWS2"
       
  2572     fi
       
  2573 fi
       
  2574 print "</td></tr>"
       
  2575 if [[ -n $rflag ]]; then
       
  2576     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
       
  2577 elif [[ -n $OUTREV ]]; then
       
  2578     if [[ -z $forestflag ]]; then
       
  2579         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
       
  2580     fi
       
  2581 fi
       
  2582 if [[ -n $HG_BRANCH ]]; then
       
  2583     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
       
  2584 fi
       
  2585 
       
  2586 print "<tr><th>Summary of changes:</th><td>"
       
  2587 printCI $TOTL $TINS $TDEL $TMOD $TUNC
       
  2588 print "</td></tr>"
       
  2589 
       
  2590 if [[ -f $WDIR/$WNAME.patch ]]; then
       
  2591   print "<tr><th>Patch of changes:</th><td>"
       
  2592   print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
       
  2593 elif [[ -f $CHANGESETPATH ]]; then
       
  2594   print "<tr><th>Changeset:</th><td>"
       
  2595   print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
       
  2596 fi
       
  2597 
       
  2598 if [[ -f $WDIR/$WNAME.pdf ]]; then
       
  2599 	print "<tr><th>Printable review:</th><td>"
       
  2600 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
       
  2601 fi
       
  2602 
       
  2603 if [[ -n "$iflag" ]]; then
       
  2604 	print "<tr><th>Author comments:</th><td><div>"
       
  2605 	cat /tmp/$$.include
       
  2606 	print "</div></td></tr>"
       
  2607 fi
       
  2608 # Add links to referenced CRs, if any
       
  2609 # URL has a <title> like:
       
  2610 # <title>[#JDK-8024688] b106-lambda: j.u.Map.merge doesn&#39;t work as specified if contains key:null pair - Java Bug System</title>
       
  2611 # we format this to:
       
  2612 # JDK-8024688: b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair
       
  2613 if [[ -n $CRID ]]; then
       
  2614     for id in $CRID
       
  2615     do
       
  2616         #add "JDK-" to raw bug id for openjdk.java.net links.
       
  2617         id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
       
  2618 
       
  2619         print "<tr><th>Bug id:</th><td>"
       
  2620         url="${BUGURL}${id}"
       
  2621 
       
  2622         if [[ -n $WGET ]]; then
       
  2623             msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\[#\(.*\)\] \(.*\) - Java Bug System<\/title>/\1 : \2/' | html_dequote | html_quote`
       
  2624         fi
       
  2625         if [[ -z $msg ]]; then
       
  2626             msg="${id}"
       
  2627         fi
       
  2628 
       
  2629         print "<a href=\"$url\">$msg</a>"
       
  2630 
       
  2631         print "</td></tr>"
       
  2632     done
       
  2633 fi
       
  2634 print "<tr><th>Legend:</th><td>"
       
  2635 print "<b>Modified file</b><br><font color=red><b>Deleted file</b></font><br><font color=green><b>New file</b></font></td></tr>"
       
  2636 print "</table>"
       
  2637 print "</div>"
       
  2638 
       
  2639 #
       
  2640 # Second pass through the files: generate the rest of the index file
       
  2641 #
       
  2642 while read LINE
       
  2643 do
       
  2644 	set - $LINE
       
  2645         if [[ $1 == "Revision:" ]]; then
       
  2646             FIRST_CREV=`expr $3 + 1`
       
  2647             continue
       
  2648         fi
       
  2649 	P=$1
       
  2650 
       
  2651 	if [[ $# == 2 ]]; then
       
  2652 		PP=$2
       
  2653 		oldname=" <i>(was $PP)</i>"
       
  2654 
       
  2655 	else
       
  2656 		PP=$P
       
  2657 		oldname=""
       
  2658 	fi
       
  2659 
       
  2660 	DIR=${P%/*}
       
  2661 	if [[ $DIR == $P ]]; then
       
  2662 		DIR="."   # File at root of workspace
       
  2663 	fi
       
  2664 
       
  2665 	# Avoid processing the same file twice.
       
  2666 	# It's possible for renamed files to
       
  2667 	# appear twice in the file list
       
  2668 
       
  2669 	F=$WDIR/$P
       
  2670 
       
  2671 	print "<p><code>"
       
  2672 
       
  2673 	# If there's a diffs file, make diffs links
       
  2674 
       
  2675         NODIFFS=
       
  2676 	if [[ -f $F.cdiff.html ]]; then
       
  2677 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
       
  2678 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
       
  2679 
       
  2680 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
       
  2681 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
       
  2682 		fi
       
  2683 
       
  2684 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
       
  2685 
       
  2686 		print "<a href=\"$P.frames.html\">Frames</a>"
       
  2687 	else
       
  2688                 NODIFFS=1
       
  2689 		print " ------ ------ ------"
       
  2690 
       
  2691 		if [[ -x $WDIFF ]]; then
       
  2692 			print " ------"
       
  2693 		fi
       
  2694 
       
  2695 		print " ------"
       
  2696 	fi
       
  2697 
       
  2698 	# If there's an old file, make the link
       
  2699 
       
  2700         NOOLD=
       
  2701 	if [[ -f $F-.html ]]; then
       
  2702 		print "<a href=\"$P-.html\">Old</a>"
       
  2703 	else
       
  2704                 NOOLD=1
       
  2705 		print " ---"
       
  2706 	fi
       
  2707 
       
  2708 	# If there's an new file, make the link
       
  2709 
       
  2710         NONEW=
       
  2711 	if [[ -f $F.html ]]; then
       
  2712 		print "<a href=\"$P.html\">New</a>"
       
  2713 	else
       
  2714                 NONEW=1
       
  2715 		print " ---"
       
  2716 	fi
       
  2717 
       
  2718 	if [[ -f $F.patch ]]; then
       
  2719 		print "<a href=\"$P.patch\">Patch</a>"
       
  2720 	else
       
  2721 		print " -----"
       
  2722 	fi
       
  2723 
       
  2724 	if [[ -f $WDIR/raw_files/new/$P ]]; then
       
  2725 		print "<a href=\"raw_files/new/$P\">Raw</a>"
       
  2726 	else
       
  2727 		print " ---"
       
  2728 	fi
       
  2729         print "</code>"
       
  2730         if [[ -n $NODIFFS && -z $oldname ]]; then
       
  2731             if [[ -n $NOOLD ]]; then
       
  2732                 print "<font color=green><b>$P</b></font>"
       
  2733             elif [[ -n $NONEW ]]; then
       
  2734                 print "<font color=red><b>$P</b></font>"
       
  2735             fi
       
  2736         else
       
  2737 	    print "<b>$P</b> $oldname"
       
  2738         fi
       
  2739 
       
  2740 	print "</p><blockquote>\c"
       
  2741 	# Insert delta comments if any
       
  2742 	comments=`getcomments html $P $PP`
       
  2743 	if [ -n "$comments" ]; then
       
  2744 	    print "<pre>$comments</pre>"
       
  2745 	fi
       
  2746 
       
  2747 	# Add additional comments comment
       
  2748 
       
  2749 	print "<!-- Add comments to explain changes in $P here -->"
       
  2750 
       
  2751 	# Add count of changes.
       
  2752 
       
  2753 	if [[ -f $F.count ]]; then
       
  2754 	    cat $F.count
       
  2755 	    rm $F.count
       
  2756 	fi
       
  2757         print "</blockquote>"
       
  2758 done < $FLIST
       
  2759 
       
  2760 print
       
  2761 print
       
  2762 print "<hr />"
       
  2763 print "<p style=\"font-size: small\">"
       
  2764 print "This code review page was prepared using <b>$0</b>"
       
  2765 print "(vers $WEBREV_UPDATED)."
       
  2766 print "</body>"
       
  2767 print "</html>"
       
  2768 
       
  2769 if [[ -n $ZIP ]]; then
       
  2770     # Let's generate a zip file for convenience
       
  2771     cd $WDIR/..
       
  2772     if [ -f webrev.zip ]; then
       
  2773 	rm webrev.zip
       
  2774     fi
       
  2775     $ZIP -r webrev webrev >/dev/null 2>&1
       
  2776 fi
       
  2777 
       
  2778 exec 1<&-			# Close FD 1.
       
  2779 exec 1<&3			# dup FD 3 to restore stdout.
       
  2780 exec 3<&-			# close FD 3.
       
  2781 
       
  2782 print "Done."
       
  2783 print "Output to: $WDIR"