Wed, 30 Oct 2013 13:41:36 -0700
Merge
1 #!/bin/ksh -p
2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22 # Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
23 # Use is subject to license terms.
24 #
25 # This script takes a file list and a workspace and builds a set of html files
26 # suitable for doing a code review of source changes via a web page.
27 # Documentation is available via 'webrev -h'.
28 #
30 WEBREV_UPDATED=25.0-hg+openjdk.java.net
32 HTML='<?xml version="1.0"?>
33 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
35 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
37 FRAMEHTML='<?xml version="1.0"?>
38 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
39 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
40 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
42 STDHEAD='<meta 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.
51 For example, to have all "removed" information be red instead of
52 brown, set a rule in your userContent.css file like:
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; }
104 </style>
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 '
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 '
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/&/\&/g" -e "s/&#\([x]*[0-9A-Fa-f]\{2,5\}\);/\&#\1;/g" -e "s/</\</g" -e "s/>/\>/g" "$@" | expand
141 }
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/"/\"/g" -e "s/'/\'/g" -e "s/&/\&/g" -e "s/</<'/g" -e "s/>/>/g" "$@" | expand
153 }
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 }
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 }
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>" }
212 ' $1
213 }
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
280 TNAME=$3
281 TPATH=$4
282 COMMENT=$5
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}
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 }
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" ;
359 next
360 }
361 }
363 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
364 ' /tmp/$$.diffs > /tmp/$$.file1
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 }
379 /^</ {next}
380 /^>/ {next}
381 /^---/ {next}
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 }
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
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
453 html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
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>"
467 strip_unchanged /tmp/$$.file1.html
469 print "</pre></td><td><pre>"
471 strip_unchanged /tmp/$$.file2.html
473 print "</pre></td>"
474 print "</tr></table>"
475 print "</body></html>"
477 framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
478 "$COMMENT"
479 }
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
504 # Enable html files to access WDIR via a relative path.
505 RTOP=$(relative_dir $TPATH $WDIR)
507 # Make the rhs/lhs files and output the frameset file.
508 print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
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
518 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
520 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
521 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
523 close='</body></html>'
525 print $close >> $WDIR/$DIR/$TNAME.lhs.html
526 print $close >> $WDIR/$DIR/$TNAME.rhs.html
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 }
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
566 cat > /tmp/$$.crmerge.pl << \EOF
568 print scalar(<>); # %!PS-Adobe---
569 print "%%Orientation: Landscape\n";
571 $pno = 0;
572 $doprint = 1;
574 $page = "";
576 while (<>) {
577 next if (/^%%Pages:\s*\d+/);
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 }
593 # Skip from %%Trailer of one document to Endprolog
594 # %%Page of the next
595 $doprint = 0 if (/^%%Trailer/);
596 $page .= $_ if ($doprint);
597 }
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
608 $PERL /tmp/$$.crmerge.pl < $infile
609 }
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 }
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();
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 }
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
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 }
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;
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 }
736 function scrollToAnc(num) {
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);
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);
754 parent.lhs.scrollBy(0,-30);
755 parent.rhs.scrollBy(0,-30);
756 }
758 function getAncValue()
759 {
760 return (parseInt(parent.nav.document.diff.real.value));
761 }
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 }
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 }
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 }
791 function stopScroll() {
792 if (scrolling==1) {
793 clearInterval(myInt);
794 scrolling=0;
795 }
796 }
798 function startScroll() {
799 stopScroll();
800 scrolling=1;
801 myInt=setInterval("scrollByPix()",10);
802 }
804 function handlePress(b) {
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 }
830 function handleRelease(b) {
831 stopScroll();
832 }
834 function keypress(ev) {
835 var keynum;
836 var keychar;
838 if (window.event) { // IE
839 keynum = ev.keyCode;
840 } else if (ev.which) { // non-IE
841 keynum = ev.which;
842 }
844 keychar = String.fromCharCode(keynum);
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 }
856 function ValidateDiffNum(){
857 val = parent.nav.document.diff.display.value;
858 if (val == "EOF") {
859 scrollToAnc(999999);
860 return;
861 }
863 if (val == "BOF") {
864 scrollToAnc(0);
865 return;
866 }
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 }
877 EOF
878 }
880 #
881 # frame_navigation
882 #
883 # Output anchor navigation file for framed sdiffs.
884 #
885 function frame_navigation
886 {
887 print "$HTML<head>$STDHEAD"
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" />
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
903 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
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>
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 }
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
995 print "$HTML<head>$STDHEAD"
996 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
998 if [[ $DIFFTYPE == "U" ]]; then
999 print "$UDIFFCSS"
1000 fi
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
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}
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 '
1032 print "</pre></body></html>\n"
1033 }
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
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 }
1054 comments_from_mercurial()
1055 {
1056 fmt=$1
1057 pfile=$PWS/$2
1058 cfile=$CWS/$3
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
1087 print "$comm" | html_quote | bug2url
1088 )
1089 fi
1090 }
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
1104 comments_from_mercurial $fmt $pp $p
1105 }
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 }
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
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;
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 }
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 }
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 }
1190 # Add lines: both Na and N,Na
1191 /^[0-9].*a$/ {
1192 while (getline != /^\.$/) a++;
1193 next;
1194 }
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 }
1212 # Delete line: Nd. For example 10d says line 10 is deleted.
1213 /^[0-9]*d$/ {d++; next}
1215 # Should not get here!
1216 {
1217 error=1;
1218 exit;
1219 }
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 }' )
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
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 }
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 }
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 }
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"
1310 $HGCMD -mdn 2>/dev/null | $FILTER | while read F
1311 do
1312 echo $TREE/$F
1313 done >> $FLIST
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 }
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
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 #
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 }
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.
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
1481 hg status $STATUS_REV -mdn | $FILTER > $FLIST
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
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 }
1532 function env_from_flist
1533 {
1534 [[ -r $FLIST ]] || return
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 = `
1543 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
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 }
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 }
1567 function look_for_prog
1568 {
1569 typeset path
1570 typeset ppath
1571 typeset progname=$1
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
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
1587 PATH=$ppath prog=`whence $progname`
1588 if [[ -n $prog ]]; then
1589 print $prog
1590 fi
1591 }
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 }
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
1626 }
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
1642 #
1643 # Old version of the file.
1644 #
1645 rm -rf $olddir/$DIR/$F
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
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
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 cd $CWS/$PDIR
1690 if [ -n "$rflag" ]; then
1691 parentrev=$PARENT_REV
1692 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1693 parentrev=$OUTREV
1694 fi
1695 if [ -z "$parentrev" ]; then
1696 parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
1697 fi
1698 if [ -n "$parentrev" ]; then
1699 mkdir -p $olddir/$PDIR
1700 if [ -z "$parent" ]; then
1701 hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
1702 else
1703 $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
1704 fi
1705 fi
1706 cd $current_dir
1707 fi
1708 }
1710 function build_old_new
1711 {
1712 if [[ $SCM_MODE == "mercurial" ]]; then
1713 build_old_new_mercurial $@
1714 fi
1715 }
1718 #
1719 # Usage message.
1720 #
1721 function usage
1722 {
1723 print "Usage:\twebrev [options]
1724 webrev [options] ( <file> | - )
1726 Options:
1727 -v: Print the version of this tool.
1728 -b: Do not ignore changes in the amount of white space.
1729 -c <CR#>: Include link to CR (aka bugid) in the main page.
1730 -i <filename>: Include <filename> in the index.html file.
1731 -o <outdir>: Output webrev to specified directory.
1732 -p <compare-against>: Use specified parent wkspc or basis for comparison
1733 -u <username>: Use that username instead of 'guessing' one.
1734 -m: Forces the use of Mercurial
1736 Mercurial only options:
1737 -r rev: Compare against a specified revision
1738 -N: Skip 'hg outgoing', use only 'hg status'
1739 -f: Use the forest extension
1741 Arguments:
1742 <file>: Optional file containing list of files to include in webrev
1743 -: read list of files to include in webrev from standard input
1745 Environment:
1746 WDIR: Control the output directory.
1747 WEBREV_BUGURL: Control the URL prefix for bugids.
1749 "
1751 exit 2
1752 }
1754 #
1755 #
1756 # Main program starts here
1757 #
1758 #
1759 LANG="C"
1760 LC_ALL="C"
1761 export LANG LC_ALL
1762 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
1764 set +o noclobber
1766 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
1767 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
1768 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
1769 [[ -z $PERL ]] && PERL=`look_for_prog perl`
1770 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
1771 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
1772 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
1773 [[ -z $AWK ]] && AWK=`look_for_prog awk`
1774 [[ -z $JAR ]] && JAR=`look_for_prog jar`
1775 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
1776 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
1777 [[ -z $WGET ]] && WGET=`look_for_prog wget`
1779 if uname | grep CYGWIN >/dev/null
1780 then
1781 ISWIN=1
1782 # Under windows mercurial outputs '\' instead of '/'
1783 FILTER="tr '\\\\' '/'"
1784 else
1785 FILTER="cat"
1786 fi
1788 if [[ ! -x $PERL ]]; then
1789 print -u2 "Error: No perl interpreter found. Exiting."
1790 exit 1
1791 fi
1793 #
1794 # These aren't fatal, but we want to note them to the user.
1795 #
1796 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
1797 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
1798 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
1800 # Declare global total counters.
1801 integer TOTL TINS TDEL TMOD TUNC
1803 flist_mode=
1804 flist_file=
1805 bflag=
1806 iflag=
1807 oflag=
1808 pflag=
1809 uflag=
1810 Oflag=
1811 rflag=
1812 Nflag=
1813 forestflag=
1814 while getopts "c:i:o:p:r:u:mONvfb" opt
1815 do
1816 case $opt in
1817 b) bflag=1;;
1819 i) iflag=1
1820 INCLUDE_FILE=$OPTARG;;
1822 o) oflag=1
1823 WDIR=$OPTARG;;
1825 p) pflag=1
1826 codemgr_parent=$OPTARG;;
1828 u) uflag=1
1829 username=$OPTARG;;
1831 c) if [[ -z $CRID ]]; then
1832 CRID=$OPTARG
1833 else
1834 CRID="$CRID $OPTARG"
1835 fi;;
1837 m) SCM_MODE="mercurial";;
1839 O) Oflag=1;; # ignored (bugs are now all visible at bugs.openjdk.java.net)
1841 N) Nflag=1;;
1843 f) forestflag=1;;
1845 r) rflag=1
1846 PARENT_REV=$OPTARG;;
1848 v) print "$0 version: $WEBREV_UPDATED";;
1851 ?) usage;;
1852 esac
1853 done
1855 FLIST=/tmp/$$.flist
1856 HG_LIST_FROM_COMMIT=
1858 if [[ -n $forestflag && -n $rflag ]]; then
1859 print "The -r <rev> flag is incompatible with the use of forests"
1860 exit 2
1861 fi
1863 #
1864 # If this manually set as the parent, and it appears to be an earlier webrev,
1865 # then note that fact and set the parent to the raw_files/new subdirectory.
1866 #
1867 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
1868 parent_webrev="$codemgr_parent"
1869 codemgr_parent="$codemgr_parent/raw_files/new"
1870 fi
1872 shift $(($OPTIND - 1))
1874 if [[ $1 == "-" ]]; then
1875 cat > $FLIST
1876 flist_mode="stdin"
1877 flist_done=1
1878 shift
1879 elif [[ -n $1 ]]; then
1880 if [[ ! -r $1 ]]; then
1881 print -u2 "$1: no such file or not readable"
1882 usage
1883 fi
1884 cat $1 > $FLIST
1885 flist_mode="file"
1886 flist_file=$1
1887 flist_done=1
1888 shift
1889 else
1890 flist_mode="auto"
1891 fi
1893 #
1894 # Before we go on to further consider -l and -w, work out which SCM we think
1895 # is in use.
1896 #
1897 if [[ -z $SCM_MODE ]]; then
1898 SCM_MODE=`detect_scm $FLIST`
1899 fi
1900 if [[ $SCM_MODE == "unknown" ]]; then
1901 print -u2 "Unable to determine SCM type currently in use."
1902 print -u2 "For mercurial: webrev runs 'hg root'."
1903 exit 1
1904 fi
1906 print -u2 " SCM detected: $SCM_MODE"
1909 if [[ $SCM_MODE == "mercurial" ]]; then
1910 #
1911 # determine Workspace and parent workspace paths
1912 #
1913 CWS=`hg root | $FILTER`
1914 if [[ -n $pflag && -z "$PWS" ]]; then
1915 OUTPWS=$codemgr_parent
1916 # Let's try to expand it if it's an alias defined in [paths]
1917 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
1918 if [[ -n $tmp ]]; then
1919 OUTPWS="$tmp"
1920 fi
1921 if [[ -n $rflag ]]; then
1922 if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
1923 PWS=$codemgr_parent
1924 else
1925 PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
1926 fi
1927 fi
1928 fi
1929 #
1930 # OUTPWS is the parent repository to use when using 'hg outgoing'
1931 #
1932 if [[ -z $Nflag ]]; then
1933 if [[ -n $forestflag ]]; then
1934 #
1935 # for forest we have to rely on properly set default and
1936 # default-push because they can be different from the top one.
1937 # unless of course it was explicitly specified with -p
1938 if [[ -z $pflag ]]; then
1939 OUTPWS=
1940 fi
1941 else
1942 #
1943 # Unfortunately mercurial is bugged and doesn't handle
1944 # aliases correctly in 'hg path default'
1945 # So let's do it ourselves. Sigh...
1946 if [[ -z "$OUTPWS" ]]; then
1947 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
1948 fi
1949 # Still empty, means no default-push
1950 if [[ -z "$OUTPWS" ]]; then
1951 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
1952 fi
1953 # Let's try to expand it if it's an alias defined in [paths]
1954 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
1955 if [[ -n $tmp ]]; then
1956 OUTPWS="$tmp"
1957 fi
1958 fi
1959 fi
1960 #
1961 # OUTPWS may contain username:password, let's make sure we remove the
1962 # sensitive information before we print out anything in the HTML
1963 #
1964 OUTPWS2=$OUTPWS
1965 if [[ -n $OUTPWS ]]; then
1966 if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
1967 # Remove everything between '://' and '@'
1968 OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
1969 fi
1970 fi
1972 if [[ -z $HG_BRANCH ]]; then
1973 HG_BRANCH=`hg branch`
1974 if [ "$HG_BRANCH" == "default" ]; then
1975 #
1976 # 'default' means no particular branch, so let's cancel that
1977 #
1978 HG_BRANCH=
1979 fi
1980 fi
1982 if [[ -z $forestflag ]]; then
1983 if [[ -z $Nflag ]]; then
1984 #
1985 # If no "-N", always do "hg outgoing" against parent
1986 # repository to determine list of outgoing revisions.
1987 #
1988 ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
1989 if [[ -n $ALL_CREV ]]; then
1990 FIRST_CREV=`echo "$ALL_CREV" | head -1`
1991 #
1992 # If no "-r", choose revision to compare against by
1993 # finding the latest revision not in the outgoing list.
1994 #
1995 if [[ -z $rflag ]]; then
1996 OUTREV=`find_outrev "$FIRST_CREV"`
1997 if [[ -n $OUTREV ]]; then
1998 HG_LIST_FROM_COMMIT=1
1999 fi
2000 fi
2001 fi
2002 elif [[ -n $rflag ]]; then
2003 #
2004 # If skipping "hg outgoing" but still comparing against a
2005 # specific revision (not the tip), set revision for comment
2006 # accumulation.
2007 #
2008 FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
2009 FIRST_CREV=`expr $FIRST_CREV + 1`
2010 fi
2011 fi
2012 #Let's check if a merge is needed, if so, issue a warning
2013 PREV=`hg parent | grep '^tag:.*tip$'`
2014 if [[ -z $PREV ]]; then
2015 print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
2016 fi
2017 fi
2019 if [[ $flist_mode == "stdin" ]]; then
2020 print -u2 " File list from: standard input"
2021 elif [[ $flist_mode == "file" ]]; then
2022 print -u2 " File list from: $flist_file"
2023 fi
2025 if [[ $# -gt 0 ]]; then
2026 print -u2 "WARNING: unused arguments: $*"
2027 fi
2029 if [[ $SCM_MODE == "mercurial" ]]; then
2030 if [[ -z $flist_done ]]; then
2031 flist_from_mercurial $PWS
2032 fi
2033 fi
2035 #
2036 # If the user didn't specify a -i option, check to see if there is a
2037 # webrev-info file in the workspace directory.
2038 #
2039 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2040 iflag=1
2041 INCLUDE_FILE="$CWS/webrev-info"
2042 fi
2044 if [[ -n $iflag ]]; then
2045 if [[ ! -r $INCLUDE_FILE ]]; then
2046 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
2047 "not readable."
2048 exit 1
2049 else
2050 #
2051 # $INCLUDE_FILE may be a relative path, and the script alters
2052 # PWD, so we just stash a copy in /tmp.
2053 #
2054 cp $INCLUDE_FILE /tmp/$$.include
2055 fi
2056 fi
2058 #
2059 # Output directory.
2060 #
2061 if [[ -z $WDIR ]]; then
2062 WDIR=$CWS/webrev
2063 else
2064 # If the output directory doesn't end with '/webrev' or '/webrev/'
2065 # then add '/webrev'. This is for backward compatibility
2066 if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
2067 then
2068 WDIR=$WDIR/webrev
2069 fi
2070 fi
2071 # WDIR=${WDIR:-$CWS/webrev}
2073 #
2074 # Name of the webrev, derived from the workspace name; in the
2075 # future this could potentially be an option.
2076 #
2077 # Let's keep what's after the last '/'
2078 WNAME=${CWS##*/}
2080 #
2081 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
2082 #
2083 if [ ${WDIR%%/*} ]; then
2084 if [[ -n $ISWIN ]]; then
2085 if [ ${WDIR%%[A-Za-z]:*} ]; then
2086 WDIR=$PWD/$WDIR
2087 fi
2088 else
2089 WDIR=$PWD/$WDIR
2090 fi
2091 fi
2093 if [[ ! -d $WDIR ]]; then
2094 mkdir -p $WDIR
2095 [[ $? != 0 ]] && exit 1
2096 fi
2098 #
2099 # Summarize what we're going to do.
2100 #
2101 print " Workspace: $CWS"
2102 if [[ -n $parent_webrev ]]; then
2103 print "Compare against: webrev at $parent_webrev"
2104 elif [[ -n $OUTPWS2 ]]; then
2105 print "Compare against: $OUTPWS2"
2106 fi
2107 if [[ -n $HG_BRANCH ]]; then
2108 print " Branch: $HG_BRANCH"
2109 fi
2110 if [[ -n $rflag ]]; then
2111 print "Compare against version: $PARENT_REV"
2112 fi
2113 [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE"
2114 print " Output to: $WDIR"
2116 #
2117 # Save the file list in the webrev dir
2118 #
2119 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
2121 #
2122 # Bug IDs will be replaced by a URL. Order of precedence
2123 # is: default location, $WEBREV_BUGURL
2124 #
2125 BUGURL='https://bugs.openjdk.java.net/browse/'
2126 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
2127 IDPREFIX='JDK-'
2130 rm -f $WDIR/$WNAME.patch
2131 rm -f $WDIR/$WNAME.changeset
2132 rm -f $WDIR/$WNAME.ps
2133 rm -f $WDIR/$WNAME.pdf
2135 touch $WDIR/$WNAME.patch
2137 print " Output Files:"
2139 #
2140 # Clean up the file list: Remove comments, blank lines and env variables.
2141 #
2142 sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean
2143 FLIST=/tmp/$$.flist.clean
2145 #
2146 # Clean up residual raw files
2147 #
2148 if [ -d $WDIR/raw_files ]; then
2149 rm -rf $WDIR/raw_files 2>/dev/null
2150 fi
2152 #
2153 # Should we ignore changes in white spaces when generating diffs?
2154 #
2155 if [[ -n $bflag ]]; then
2156 DIFFOPTS="-t"
2157 else
2158 DIFFOPTS="-bt"
2159 fi
2160 #
2161 # First pass through the files: generate the per-file webrev HTML-files.
2162 #
2163 while read LINE
2164 do
2165 set - $LINE
2166 P=$1
2168 if [[ $1 == "Revision:" ]]; then
2169 OUTREV=$2
2170 continue
2171 fi
2172 #
2173 # Normally, each line in the file list is just a pathname of a
2174 # file that has been modified or created in the child. A file
2175 # that is renamed in the child workspace has two names on the
2176 # line: new name followed by the old name.
2177 #
2178 oldname=""
2179 oldpath=""
2180 rename=
2181 if [[ $# -eq 2 ]]; then
2182 PP=$2 # old filename
2183 oldname=" (was $PP)"
2184 oldpath="$PP"
2185 rename=1
2186 PDIR=${PP%/*}
2187 if [[ $PDIR == $PP ]]; then
2188 PDIR="." # File at root of workspace
2189 fi
2191 PF=${PP##*/}
2193 DIR=${P%/*}
2194 if [[ $DIR == $P ]]; then
2195 DIR="." # File at root of workspace
2196 fi
2198 F=${P##*/}
2199 else
2200 DIR=${P%/*}
2201 if [[ "$DIR" == "$P" ]]; then
2202 DIR="." # File at root of workspace
2203 fi
2205 F=${P##*/}
2207 PP=$P
2208 PDIR=$DIR
2209 PF=$F
2210 fi
2212 # Make the webrev directory if necessary as it may have been
2213 # removed because it was empty
2214 if [ ! -d $CWS/$DIR ]; then
2215 mkdir -p $CWS/$DIR
2216 fi
2218 COMM=`getcomments html $P $PP`
2220 print "\t$P$oldname\n\t\t\c"
2222 # Make the webrev mirror directory if necessary
2223 mkdir -p $WDIR/$DIR
2225 # cd to the directory so the names are short
2226 cd $CWS/$DIR
2228 #
2229 # We stash old and new files into parallel directories in /tmp
2230 # and do our diffs there. This makes it possible to generate
2231 # clean looking diffs which don't have absolute paths present.
2232 #
2233 olddir=$WDIR/raw_files/old
2234 newdir=$WDIR/raw_files/new
2235 mkdir -p $olddir
2236 mkdir -p $newdir
2237 mkdir -p $olddir/$PDIR
2238 mkdir -p $newdir/$DIR
2240 build_old_new $olddir $newdir $DIR $F
2242 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
2243 print "*** Error: file not in parent or child"
2244 continue
2245 fi
2247 cd $WDIR/raw_files
2248 ofile=old/$PDIR/$PF
2249 nfile=new/$DIR/$F
2251 mv_but_nodiff=
2252 cmp $ofile $nfile > /dev/null 2>&1
2253 if [[ $? == 0 && $rename == 1 ]]; then
2254 mv_but_nodiff=1
2255 fi
2257 #
2258 # Cleaning up
2259 #
2260 rm -f $WDIR/$DIR/$F.cdiff.html
2261 rm -f $WDIR/$DIR/$F.udiff.html
2262 rm -f $WDIR/$DIR/$F.wdiff.html
2263 rm -f $WDIR/$DIR/$F.sdiff.html
2264 rm -f $WDIR/$DIR/$F-.html
2265 rm -f $WDIR/$DIR/$F.html
2267 its_a_jar=
2268 if expr $F : '.*\.jar' \| $F : '.*\.zip' >/dev/null; then
2269 its_a_jar=1
2270 # It's a JAR or ZIP file, let's do it differently
2271 if [[ -z $JAR ]]; then
2272 print "No access to jar, so can't produce diffs for jar or zip files"
2273 else
2274 if [ -f $ofile ]; then
2275 $JAR -tvf $ofile >"$ofile".lst
2276 fi
2277 if [ -f $nfile ]; then
2278 $JAR -tvf $nfile >"$nfile".lst
2279 fi
2281 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2283 ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
2284 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2285 > $WDIR/$DIR/$F.cdiff.html
2286 print " cdiffs\c"
2288 ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
2289 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2290 > $WDIR/$DIR/$F.udiff.html
2292 print " udiffs\c"
2294 if [[ -x $WDIFF ]]; then
2295 $WDIFF -c "$COMM" \
2296 -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
2297 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2298 if [[ $? -eq 0 ]]; then
2299 print " wdiffs\c"
2300 else
2301 print " wdiffs[fail]\c"
2302 fi
2303 fi
2305 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2306 > $WDIR/$DIR/$F.sdiff.html
2307 print " sdiffs\c"
2309 print " frames\c"
2311 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2313 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
2315 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2316 # renamed file: may also have differences
2317 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
2318 elif [[ -f $nfile ]]; then
2319 # new file: count added lines
2320 difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
2321 elif [[ -f $ofile ]]; then
2322 # old file: count deleted lines
2323 difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
2324 fi
2325 fi
2326 else
2328 #
2329 # If we have old and new versions of the file then run the
2330 # appropriate diffs. This is complicated by a couple of factors:
2331 #
2332 # - renames must be handled specially: we emit a 'remove'
2333 # diff and an 'add' diff
2334 # - new files and deleted files must be handled specially
2335 # - Solaris patch(1m) can't cope with file creation
2336 # (and hence renames) as of this writing.
2337 # - To make matters worse, gnu patch doesn't interpret the
2338 # output of Solaris diff properly when it comes to
2339 # adds and deletes. We need to do some "cleansing"
2340 # transformations:
2341 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@
2342 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@
2343 #
2344 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
2345 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
2347 if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]];
2348 then
2349 # Only need to generate a patch file here if there are no commits in outgoing
2350 # or if we've specified a file list
2351 rm -f $WDIR/$DIR/$F.patch
2352 if [[ -z $rename ]]; then
2353 if [ ! -f $ofile ]; then
2354 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2355 > $WDIR/$DIR/$F.patch
2356 elif [ ! -f $nfile ]; then
2357 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2358 > $WDIR/$DIR/$F.patch
2359 else
2360 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
2361 fi
2362 else
2363 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2364 > $WDIR/$DIR/$F.patch
2366 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2367 >> $WDIR/$DIR/$F.patch
2369 fi
2372 #
2373 # Tack the patch we just made onto the accumulated patch for the
2374 # whole wad.
2375 #
2376 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
2377 fi
2379 print " patch\c"
2381 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2383 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
2384 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2385 > $WDIR/$DIR/$F.cdiff.html
2386 print " cdiffs\c"
2388 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
2389 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2390 > $WDIR/$DIR/$F.udiff.html
2392 print " udiffs\c"
2394 if [[ -x $WDIFF ]]; then
2395 $WDIFF -c "$COMM" \
2396 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
2397 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2398 if [[ $? -eq 0 ]]; then
2399 print " wdiffs\c"
2400 else
2401 print " wdiffs[fail]\c"
2402 fi
2403 fi
2405 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2406 > $WDIR/$DIR/$F.sdiff.html
2407 print " sdiffs\c"
2409 print " frames\c"
2411 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2413 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2415 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2416 # renamed file: may also have differences
2417 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2418 elif [[ -f $nfile ]]; then
2419 # new file: count added lines
2420 difflines /dev/null $nfile > $WDIR/$DIR/$F.count
2421 elif [[ -f $ofile ]]; then
2422 # old file: count deleted lines
2423 difflines $ofile /dev/null > $WDIR/$DIR/$F.count
2424 fi
2425 fi
2426 #
2427 # Now we generate the postscript for this file. We generate diffs
2428 # only in the event that there is delta, or the file is new (it seems
2429 # tree-killing to print out the contents of deleted files).
2430 #
2431 if [[ -f $nfile ]]; then
2432 ocr=$ofile
2433 [[ ! -f $ofile ]] && ocr=/dev/null
2435 if [[ -z $mv_but_nodiff ]]; then
2436 textcomm=`getcomments text $P $PP`
2437 if [[ -x $CODEREVIEW ]]; then
2438 $CODEREVIEW -y "$textcomm" \
2439 -e $ocr $nfile \
2440 > /tmp/$$.psfile 2>/dev/null &&
2441 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
2442 if [[ $? -eq 0 ]]; then
2443 print " ps\c"
2444 else
2445 print " ps[fail]\c"
2446 fi
2447 fi
2448 fi
2449 fi
2451 if [[ -f $ofile && -z $mv_but_nodiff ]]; then
2452 if [[ -n $its_a_jar ]]; then
2453 source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
2454 else
2455 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
2456 fi
2457 print " old\c"
2458 fi
2460 if [[ -f $nfile ]]; then
2461 if [[ -n $its_a_jar ]]; then
2462 source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
2463 else
2464 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
2465 fi
2466 print " new\c"
2467 fi
2469 print
2470 done < $FLIST
2472 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
2473 if [[ $SCM_MODE == "mercurial" ]]; then
2474 if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then
2475 EXPORTCHANGESET="$WNAME.changeset"
2476 CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
2477 rm -f $CHANGESETPATH
2478 touch $CHANGESETPATH
2479 if [[ -n $ALL_CREV ]]; then
2480 rev_opt=
2481 for rev in $ALL_CREV; do
2482 rev_opt="$rev_opt --rev $rev"
2483 done
2484 elif [[ -n $FIRST_CREV ]]; then
2485 rev_opt="--rev $FIRST_CREV"
2486 fi
2488 if [[ -n $rev_opt ]]; then
2489 (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
2490 echo "Created changeset: $CHANGESETPATH" 1>&2
2491 # Use it in place of the jdk.patch created above
2492 rm -f $WDIR/$WNAME.patch
2493 fi
2494 set +x
2495 fi
2496 fi
2498 frame_nav_js > $WDIR/ancnav.js
2499 frame_navigation > $WDIR/ancnav.html
2501 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
2502 print " Generating PDF: \c"
2503 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
2504 print "Done."
2505 fi
2507 # Now build the index.html file that contains
2508 # links to the source files and their diffs.
2510 cd $CWS
2512 # Save total changed lines for Code Inspection.
2513 print "$TOTL" > $WDIR/TotalChangedLines
2515 print " index.html: \c"
2516 INDEXFILE=$WDIR/index.html
2517 exec 3<&1 # duplicate stdout to FD3.
2518 exec 1<&- # Close stdout.
2519 exec > $INDEXFILE # Open stdout to index file.
2521 print "$HTML<head>"
2522 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
2523 print "$STDHEAD"
2524 print "<title>$WNAME</title>"
2525 print "</head>"
2526 print "<body id=\"SUNWwebrev\">"
2527 print "<div class=\"summary\">"
2528 print "<h2>Code Review for $WNAME</h2>"
2530 print "<table>"
2532 if [[ -z $uflag ]]; then
2533 if [[ $SCM_MODE == "mercurial" ]]; then
2534 #
2535 # Let's try to extract the user name from the .hgrc file
2536 #
2537 username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
2538 fi
2540 if [[ -z $username ]]; then
2541 #
2542 # Figure out the username and gcos name. To maintain compatibility
2543 # with passwd(4), we must support '&' substitutions.
2544 #
2545 username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
2546 if [[ -x $GETENT ]]; then
2547 realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
2548 fi
2549 userupper=`print "$username" | sed 's/\<./\u&/g'`
2550 realname=`print $realname | sed s/\&/$userupper/`
2551 fi
2552 fi
2554 date="on `date`"
2556 if [[ -n "$username" && -n "$realname" ]]; then
2557 print "<tr><th>Prepared by:</th>"
2558 print "<td>$realname ($username) $date</td></tr>"
2559 elif [[ -n "$username" ]]; then
2560 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
2561 fi
2563 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
2564 if [[ -n $parent_webrev ]]; then
2565 print "<tr><th>Compare against:</th><td>"
2566 print "webrev at $parent_webrev"
2567 else
2568 if [[ -n $OUTPWS2 ]]; then
2569 print "<tr><th>Compare against:</th><td>"
2570 print "$OUTPWS2"
2571 fi
2572 fi
2573 print "</td></tr>"
2574 if [[ -n $rflag ]]; then
2575 print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
2576 elif [[ -n $OUTREV ]]; then
2577 if [[ -z $forestflag ]]; then
2578 print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
2579 fi
2580 fi
2581 if [[ -n $HG_BRANCH ]]; then
2582 print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
2583 fi
2585 print "<tr><th>Summary of changes:</th><td>"
2586 printCI $TOTL $TINS $TDEL $TMOD $TUNC
2587 print "</td></tr>"
2589 if [[ -f $WDIR/$WNAME.patch ]]; then
2590 print "<tr><th>Patch of changes:</th><td>"
2591 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
2592 elif [[ -f $CHANGESETPATH ]]; then
2593 print "<tr><th>Changeset:</th><td>"
2594 print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
2595 fi
2597 if [[ -f $WDIR/$WNAME.pdf ]]; then
2598 print "<tr><th>Printable review:</th><td>"
2599 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
2600 fi
2602 if [[ -n "$iflag" ]]; then
2603 print "<tr><th>Author comments:</th><td><div>"
2604 cat /tmp/$$.include
2605 print "</div></td></tr>"
2606 fi
2607 # Add links to referenced CRs, if any
2608 # URL has a <title> like:
2609 # <title>[#JDK-8024688] b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair - Java Bug System</title>
2610 # we format this to:
2611 # JDK-8024688: b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair
2612 if [[ -n $CRID ]]; then
2613 for id in $CRID
2614 do
2615 #add "JDK-" to raw bug id for openjdk.java.net links.
2616 id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
2618 print "<tr><th>Bug id:</th><td>"
2619 url="${BUGURL}${id}"
2621 if [[ -n $WGET ]]; then
2622 msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\[#\(.*\)\] \(.*\) - Java Bug System<\/title>/\1 : \2/' | html_dequote | html_quote`
2623 fi
2624 if [[ -z $msg ]]; then
2625 msg="${id}"
2626 fi
2628 print "<a href=\"$url\">$msg</a>"
2630 print "</td></tr>"
2631 done
2632 fi
2633 print "<tr><th>Legend:</th><td>"
2634 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>"
2635 print "</table>"
2636 print "</div>"
2638 #
2639 # Second pass through the files: generate the rest of the index file
2640 #
2641 while read LINE
2642 do
2643 set - $LINE
2644 if [[ $1 == "Revision:" ]]; then
2645 FIRST_CREV=`expr $3 + 1`
2646 continue
2647 fi
2648 P=$1
2650 if [[ $# == 2 ]]; then
2651 PP=$2
2652 oldname=" <i>(was $PP)</i>"
2654 else
2655 PP=$P
2656 oldname=""
2657 fi
2659 DIR=${P%/*}
2660 if [[ $DIR == $P ]]; then
2661 DIR="." # File at root of workspace
2662 fi
2664 # Avoid processing the same file twice.
2665 # It's possible for renamed files to
2666 # appear twice in the file list
2668 F=$WDIR/$P
2670 print "<p><code>"
2672 # If there's a diffs file, make diffs links
2674 NODIFFS=
2675 if [[ -f $F.cdiff.html ]]; then
2676 print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
2677 print "<a href=\"$P.udiff.html\">Udiffs</a>"
2679 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
2680 print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
2681 fi
2683 print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
2685 print "<a href=\"$P.frames.html\">Frames</a>"
2686 else
2687 NODIFFS=1
2688 print " ------ ------ ------"
2690 if [[ -x $WDIFF ]]; then
2691 print " ------"
2692 fi
2694 print " ------"
2695 fi
2697 # If there's an old file, make the link
2699 NOOLD=
2700 if [[ -f $F-.html ]]; then
2701 print "<a href=\"$P-.html\">Old</a>"
2702 else
2703 NOOLD=1
2704 print " ---"
2705 fi
2707 # If there's an new file, make the link
2709 NONEW=
2710 if [[ -f $F.html ]]; then
2711 print "<a href=\"$P.html\">New</a>"
2712 else
2713 NONEW=1
2714 print " ---"
2715 fi
2717 if [[ -f $F.patch ]]; then
2718 print "<a href=\"$P.patch\">Patch</a>"
2719 else
2720 print " -----"
2721 fi
2723 if [[ -f $WDIR/raw_files/new/$P ]]; then
2724 print "<a href=\"raw_files/new/$P\">Raw</a>"
2725 else
2726 print " ---"
2727 fi
2728 print "</code>"
2729 if [[ -n $NODIFFS && -z $oldname ]]; then
2730 if [[ -n $NOOLD ]]; then
2731 print "<font color=green><b>$P</b></font>"
2732 elif [[ -n $NONEW ]]; then
2733 print "<font color=red><b>$P</b></font>"
2734 fi
2735 else
2736 print "<b>$P</b> $oldname"
2737 fi
2739 print "</p><blockquote>\c"
2740 # Insert delta comments if any
2741 comments=`getcomments html $P $PP`
2742 if [ -n "$comments" ]; then
2743 print "<pre>$comments</pre>"
2744 fi
2746 # Add additional comments comment
2748 print "<!-- Add comments to explain changes in $P here -->"
2750 # Add count of changes.
2752 if [[ -f $F.count ]]; then
2753 cat $F.count
2754 rm $F.count
2755 fi
2756 print "</blockquote>"
2757 done < $FLIST
2759 print
2760 print
2761 print "<hr />"
2762 print "<p style=\"font-size: small\">"
2763 print "This code review page was prepared using <b>$0</b>"
2764 print "(vers $WEBREV_UPDATED)."
2765 print "</body>"
2766 print "</html>"
2768 if [[ -n $ZIP ]]; then
2769 # Let's generate a zip file for convenience
2770 cd $WDIR/..
2771 if [ -f webrev.zip ]; then
2772 rm webrev.zip
2773 fi
2774 $ZIP -r webrev webrev >/dev/null 2>&1
2775 fi
2777 exec 1<&- # Close FD 1.
2778 exec 1<&3 # dup FD 3 to restore stdout.
2779 exec 3<&- # close FD 3.
2781 print "Done."
2782 print "Output to: $WDIR"