make/scripts/webrev.ksh

changeset 0
75a576e87639
child 1133
50aaf272884f
equal deleted inserted replaced
-1:000000000000 0:75a576e87639
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"

mercurial