Fri, 17 May 2013 10:06:56 -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=24.0-hg+jbs
32 HTML='<?xml version="1.0"?>
33 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
35 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
37 FRAMEHTML='<?xml version="1.0"?>
38 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
39 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
40 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
42 STDHEAD='<meta http-equiv="cache-control" content="no-cache" />
43 <meta http-equiv="Pragma" content="no-cache" />
44 <meta http-equiv="Expires" content="-1" />
45 <!--
46 Note to customizers: the body of the webrev is IDed as SUNWwebrev
47 to allow easy overriding by users of webrev via the userContent.css
48 mechanism available in some browsers.
50 For example, to have all "removed" information be red instead of
51 brown, set a rule in your userContent.css file like:
53 body#SUNWwebrev span.removed { color: red ! important; }
54 -->
55 <style type="text/css" media="screen">
56 body {
57 background-color: #eeeeee;
58 }
59 hr {
60 border: none 0;
61 border-top: 1px solid #aaa;
62 height: 1px;
63 }
64 div.summary {
65 font-size: .8em;
66 border-bottom: 1px solid #aaa;
67 padding-left: 1em;
68 padding-right: 1em;
69 }
70 div.summary h2 {
71 margin-bottom: 0.3em;
72 }
73 div.summary table th {
74 text-align: right;
75 vertical-align: top;
76 white-space: nowrap;
77 }
78 span.lineschanged {
79 font-size: 0.7em;
80 }
81 span.oldmarker {
82 color: red;
83 font-size: large;
84 font-weight: bold;
85 }
86 span.newmarker {
87 color: green;
88 font-size: large;
89 font-weight: bold;
90 }
91 span.removed {
92 color: brown;
93 }
94 span.changed {
95 color: blue;
96 }
97 span.new {
98 color: blue;
99 font-weight: bold;
100 }
101 a.print { font-size: x-small; }
103 </style>
105 <style type="text/css" media="print">
106 pre { font-size: 0.8em; font-family: courier, monospace; }
107 span.removed { color: #444; font-style: italic }
108 span.changed { font-weight: bold; }
109 span.new { font-weight: bold; }
110 span.newmarker { font-size: 1.2em; font-weight: bold; }
111 span.oldmarker { font-size: 1.2em; font-weight: bold; }
112 a.print {display: none}
113 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
114 </style>
115 '
117 #
118 # UDiffs need a slightly different CSS rule for 'new' items (we don't
119 # want them to be bolded as we do in cdiffs or sdiffs).
120 #
121 UDIFFCSS='
122 <style type="text/css" media="screen">
123 span.new {
124 color: blue;
125 font-weight: normal;
126 }
127 </style>
128 '
130 #
131 # input_cmd | html_quote | output_cmd
132 # or
133 # html_quote filename | output_cmd
134 #
135 # Make a piece of source code safe for display in an HTML <pre> block.
136 #
137 html_quote()
138 {
139 sed -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" "$@" | expand
140 }
142 #
143 # input_cmd | bug2url | output_cmd
144 #
145 # Scan for bugids and insert <a> links to the relevent bug database.
146 #
147 bug2url()
148 {
149 sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL$IDPREFIX'&\">&</a>|g'
150 }
152 #
153 # input_cmd | sac2url | output_cmd
154 #
155 # Scan for ARC cases and insert <a> links to the relevent SAC database.
156 # This is slightly complicated because inside the SWAN, SAC cases are
157 # grouped by ARC: PSARC/2006/123. But on OpenSolaris.org, they are
158 # referenced as 2006/123 (without labelling the ARC).
159 #
160 sac2url()
161 {
162 if [[ -z $Oflag ]]; then
163 sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'\1/\2/\3\">\1 \2/\3</a>|g'
164 else
165 sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'/\2/\3\">\1 \2/\3</a>|g'
166 fi
167 }
169 #
170 # strip_unchanged <infile> | output_cmd
171 #
172 # Removes chunks of sdiff documents that have not changed. This makes it
173 # easier for a code reviewer to find the bits that have changed.
174 #
175 # Deleted lines of text are replaced by a horizontal rule. Some
176 # identical lines are retained before and after the changed lines to
177 # provide some context. The number of these lines is controlled by the
178 # variable C in the $AWK script below.
179 #
180 # The script detects changed lines as any line that has a "<span class="
181 # string embedded (unchanged lines have no particular class and are not
182 # part of a <span>). Blank lines (without a sequence number) are also
183 # detected since they flag lines that have been inserted or deleted.
184 #
185 strip_unchanged()
186 {
187 $AWK '
188 BEGIN { C = c = 20 }
189 NF == 0 || /span class=/ {
190 if (c > C) {
191 c -= C
192 inx = 0
193 if (c > C) {
194 print "\n</pre><hr></hr><pre>"
195 inx = c % C
196 c = C
197 }
199 for (i = 0; i < c; i++)
200 print ln[(inx + i) % C]
201 }
202 c = 0;
203 print
204 next
205 }
206 { if (c >= C) {
207 ln[c % C] = $0
208 c++;
209 next;
210 }
211 c++;
212 print
213 }
214 END { if (c > (C * 2)) print "\n</pre><hr></hr>" }
216 ' $1
217 }
219 #
220 # sdiff_to_html
221 #
222 # This function takes two files as arguments, obtains their diff, and
223 # processes the diff output to present the files as an HTML document with
224 # the files displayed side-by-side, differences shown in color. It also
225 # takes a delta comment, rendered as an HTML snippet, as the third
226 # argument. The function takes two files as arguments, then the name of
227 # file, the path, and the comment. The HTML will be delivered on stdout,
228 # e.g.
229 #
230 # $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
231 # new/usr/src/tools/scripts/webrev.sh \
232 # webrev.sh usr/src/tools/scripts \
233 # '<a href="https://jbs.oracle.com/bugs/browse/JDK-1234567">
234 # JDK-1234567</a> my bugid' > <file>.html
235 #
236 # framed_sdiff() is then called which creates $2.frames.html
237 # in the webrev tree.
238 #
239 # FYI: This function is rather unusual in its use of awk. The initial
240 # diff run produces conventional diff output showing changed lines mixed
241 # with editing codes. The changed lines are ignored - we're interested in
242 # the editing codes, e.g.
243 #
244 # 8c8
245 # 57a61
246 # 63c66,76
247 # 68,93d80
248 # 106d90
249 # 108,110d91
250 #
251 # These editing codes are parsed by the awk script and used to generate
252 # another awk script that generates HTML, e.g the above lines would turn
253 # into something like this:
254 #
255 # BEGIN { printf "<pre>\n" }
256 # function sp(n) {for (i=0;i<n;i++)printf "\n"}
257 # function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
258 # NR==8 {wl("#7A7ADD");next}
259 # NR==54 {wl("#7A7ADD");sp(3);next}
260 # NR==56 {wl("#7A7ADD");next}
261 # NR==57 {wl("black");printf "\n"; next}
262 # : :
263 #
264 # This script is then run on the original source file to generate the
265 # HTML that corresponds to the source file.
266 #
267 # The two HTML files are then combined into a single piece of HTML that
268 # uses an HTML table construct to present the files side by side. You'll
269 # notice that the changes are color-coded:
270 #
271 # black - unchanged lines
272 # blue - changed lines
273 # bold blue - new lines
274 # brown - deleted lines
275 #
276 # Blank lines are inserted in each file to keep unchanged lines in sync
277 # (side-by-side). This format is familiar to users of sdiff(1) or
278 # Teamware's filemerge tool.
279 #
280 sdiff_to_html()
281 {
282 diff -b $1 $2 > /tmp/$$.diffs
284 TNAME=$3
285 TPATH=$4
286 COMMENT=$5
288 #
289 # Now we have the diffs, generate the HTML for the old file.
290 #
291 $AWK '
292 BEGIN {
293 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
294 printf "function removed() "
295 printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
296 printf "function changed() "
297 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
298 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
299 }
300 /^</ {next}
301 /^>/ {next}
302 /^---/ {next}
304 {
305 split($1, a, /[cad]/) ;
306 if (index($1, "a")) {
307 if (a[1] == 0) {
308 n = split(a[2], r, /,/);
309 if (n == 1)
310 printf "BEGIN\t\t{sp(1)}\n"
311 else
312 printf "BEGIN\t\t{sp(%d)}\n",\
313 (r[2] - r[1]) + 1
314 next
315 }
317 printf "NR==%s\t\t{", a[1]
318 n = split(a[2], r, /,/);
319 s = r[1];
320 if (n == 1)
321 printf "bl();printf \"\\n\"; next}\n"
322 else {
323 n = r[2] - r[1]
324 printf "bl();sp(%d);next}\n",\
325 (r[2] - r[1]) + 1
326 }
327 next
328 }
329 if (index($1, "d")) {
330 n = split(a[1], r, /,/);
331 n1 = r[1]
332 n2 = r[2]
333 if (n == 1)
334 printf "NR==%s\t\t{removed(); next}\n" , n1
335 else
336 printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
337 next
338 }
339 if (index($1, "c")) {
340 n = split(a[1], r, /,/);
341 n1 = r[1]
342 n2 = r[2]
343 final = n2
344 d1 = 0
345 if (n == 1)
346 printf "NR==%s\t\t{changed();" , n1
347 else {
348 d1 = n2 - n1
349 printf "NR==%s,NR==%s\t{changed();" , n1, n2
350 }
351 m = split(a[2], r, /,/);
352 n1 = r[1]
353 n2 = r[2]
354 if (m > 1) {
355 d2 = n2 - n1
356 if (d2 > d1) {
357 if (n > 1) printf "if (NR==%d)", final
358 printf "sp(%d);", d2 - d1
359 }
360 }
361 printf "next}\n" ;
363 next
364 }
365 }
367 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
368 ' /tmp/$$.diffs > /tmp/$$.file1
370 #
371 # Now generate the HTML for the new file
372 #
373 $AWK '
374 BEGIN {
375 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
376 printf "function new() "
377 printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
378 printf "function changed() "
379 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
380 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
381 }
383 /^</ {next}
384 /^>/ {next}
385 /^---/ {next}
387 {
388 split($1, a, /[cad]/) ;
389 if (index($1, "d")) {
390 if (a[2] == 0) {
391 n = split(a[1], r, /,/);
392 if (n == 1)
393 printf "BEGIN\t\t{sp(1)}\n"
394 else
395 printf "BEGIN\t\t{sp(%d)}\n",\
396 (r[2] - r[1]) + 1
397 next
398 }
400 printf "NR==%s\t\t{", a[2]
401 n = split(a[1], r, /,/);
402 s = r[1];
403 if (n == 1)
404 printf "bl();printf \"\\n\"; next}\n"
405 else {
406 n = r[2] - r[1]
407 printf "bl();sp(%d);next}\n",\
408 (r[2] - r[1]) + 1
409 }
410 next
411 }
412 if (index($1, "a")) {
413 n = split(a[2], r, /,/);
414 n1 = r[1]
415 n2 = r[2]
416 if (n == 1)
417 printf "NR==%s\t\t{new() ; next}\n" , n1
418 else
419 printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
420 next
421 }
422 if (index($1, "c")) {
423 n = split(a[2], r, /,/);
424 n1 = r[1]
425 n2 = r[2]
426 final = n2
427 d2 = 0;
428 if (n == 1) {
429 final = n1
430 printf "NR==%s\t\t{changed();" , n1
431 } else {
432 d2 = n2 - n1
433 printf "NR==%s,NR==%s\t{changed();" , n1, n2
434 }
435 m = split(a[1], r, /,/);
436 n1 = r[1]
437 n2 = r[2]
438 if (m > 1) {
439 d1 = n2 - n1
440 if (d1 > d2) {
441 if (n > 1) printf "if (NR==%d)", final
442 printf "sp(%d);", d1 - d2
443 }
444 }
445 printf "next}\n" ;
446 next
447 }
448 }
449 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
450 ' /tmp/$$.diffs > /tmp/$$.file2
452 #
453 # Post-process the HTML files by running them back through $AWK
454 #
455 html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
457 html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
459 #
460 # Now combine into a valid HTML file and side-by-side into a table
461 #
462 print "$HTML<head>$STDHEAD"
463 print "<title>$WNAME Sdiff $TPATH </title>"
464 print "</head><body id=\"SUNWwebrev\">"
465 print "<h2>$TPATH/$TNAME</h2>"
466 print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
467 print "<pre>$COMMENT</pre>\n"
468 print "<table><tr valign=\"top\">"
469 print "<td><pre>"
471 strip_unchanged /tmp/$$.file1.html
473 print "</pre></td><td><pre>"
475 strip_unchanged /tmp/$$.file2.html
477 print "</pre></td>"
478 print "</tr></table>"
479 print "</body></html>"
481 framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
482 "$COMMENT"
483 }
486 #
487 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
488 #
489 # Expects lefthand and righthand side html files created by sdiff_to_html.
490 # We use insert_anchors() to augment those with HTML navigation anchors,
491 # and then emit the main frame. Content is placed into:
492 #
493 # $WDIR/DIR/$TNAME.lhs.html
494 # $WDIR/DIR/$TNAME.rhs.html
495 # $WDIR/DIR/$TNAME.frames.html
496 #
497 # NOTE: We rely on standard usage of $WDIR and $DIR.
498 #
499 function framed_sdiff
500 {
501 typeset TNAME=$1
502 typeset TPATH=$2
503 typeset lhsfile=$3
504 typeset rhsfile=$4
505 typeset comments=$5
506 typeset RTOP
508 # Enable html files to access WDIR via a relative path.
509 RTOP=$(relative_dir $TPATH $WDIR)
511 # Make the rhs/lhs files and output the frameset file.
512 print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
514 cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
515 <script type="text/javascript" src="$RTOP/ancnav.js"></script>
516 </head>
517 <body id="SUNWwebrev" onkeypress="keypress(event);">
518 <a name="0"></a>
519 <pre>$comments</pre><hr></hr>
520 EOF
522 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
524 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
525 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
527 close='</body></html>'
529 print $close >> $WDIR/$DIR/$TNAME.lhs.html
530 print $close >> $WDIR/$DIR/$TNAME.rhs.html
532 print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
533 print "<title>$WNAME Framed-Sdiff " \
534 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
535 cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
536 <frameset rows="*,60">
537 <frameset cols="50%,50%">
538 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs" />
539 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs" />
540 </frameset>
541 <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0"
542 marginheight="0" name="nav" />
543 <noframes>
544 <body id="SUNWwebrev">
545 Alas 'frames' webrev requires that your browser supports frames
546 and has the feature enabled.
547 </body>
548 </noframes>
549 </frameset>
550 </html>
551 EOF
552 }
555 #
556 # fix_postscript
557 #
558 # Merge codereview output files to a single conforming postscript file, by:
559 # - removing all extraneous headers/trailers
560 # - making the page numbers right
561 # - removing pages devoid of contents which confuse some
562 # postscript readers.
563 #
564 # From Casper.
565 #
566 function fix_postscript
567 {
568 infile=$1
570 cat > /tmp/$$.crmerge.pl << \EOF
572 print scalar(<>); # %!PS-Adobe---
573 print "%%Orientation: Landscape\n";
575 $pno = 0;
576 $doprint = 1;
578 $page = "";
580 while (<>) {
581 next if (/^%%Pages:\s*\d+/);
583 if (/^%%Page:/) {
584 if ($pno == 0 || $page =~ /\)S/) {
585 # Header or single page containing text
586 print "%%Page: ? $pno\n" if ($pno > 0);
587 print $page;
588 $pno++;
589 } else {
590 # Empty page, skip it.
591 }
592 $page = "";
593 $doprint = 1;
594 next;
595 }
597 # Skip from %%Trailer of one document to Endprolog
598 # %%Page of the next
599 $doprint = 0 if (/^%%Trailer/);
600 $page .= $_ if ($doprint);
601 }
603 if ($page =~ /\)S/) {
604 print "%%Page: ? $pno\n";
605 print $page;
606 } else {
607 $pno--;
608 }
609 print "%%Trailer\n%%Pages: $pno\n";
610 EOF
612 $PERL /tmp/$$.crmerge.pl < $infile
613 }
616 #
617 # input_cmd | insert_anchors | output_cmd
618 #
619 # Flag blocks of difference with sequentially numbered invisible
620 # anchors. These are used to drive the frames version of the
621 # sdiffs output.
622 #
623 # NOTE: Anchor zero flags the top of the file irrespective of changes,
624 # an additional anchor is also appended to flag the bottom.
625 #
626 # The script detects changed lines as any line that has a "<span
627 # class=" string embedded (unchanged lines have no class set and are
628 # not part of a <span>. Blank lines (without a sequence number)
629 # are also detected since they flag lines that have been inserted or
630 # deleted.
631 #
632 function insert_anchors
633 {
634 $AWK '
635 function ia() {
636 # This should be able to be a singleton <a /> but that
637 # seems to trigger a bug in firefox a:hover rule processing
638 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
639 }
641 BEGIN {
642 anc=1;
643 inblock=1;
644 printf "<pre>\n";
645 }
646 NF == 0 || /^<span class=/ {
647 if (inblock == 0) {
648 ia();
649 inblock=1;
650 }
651 print;
652 next;
653 }
654 {
655 inblock=0;
656 print;
657 }
658 END {
659 ia();
661 printf "<b style=\"font-size: large; color: red\">";
662 printf "--- EOF ---</b>"
663 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
664 printf "</pre>"
665 printf "<form name=\"eof\">";
666 printf "<input name=\"value\" value=\"%d\" type=\"hidden\" />",
667 anc - 1;
668 printf "</form>";
669 }
670 ' $1
671 }
674 #
675 # relative_dir
676 #
677 # Print a relative return path from $1 to $2. For example if
678 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
679 # this function would print "../../../../".
680 #
681 # In the event that $1 is not in $2 a warning is printed to stderr,
682 # and $2 is returned-- the result of this is that the resulting webrev
683 # is not relocatable.
684 #
685 function relative_dir
686 {
687 d1=$1
688 d2=$2
689 if [[ "$d1" == "." ]]; then
690 print "."
691 else
692 typeset cur="${d1##$d2?(/)}"
693 typeset ret=""
694 if [[ $d2 == $cur ]]; then # Should never happen.
695 # Should never happen.
696 print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
697 print -u2 "to \"$2\". Check input paths. Framed webrev "
698 print -u2 "will not be relocatable!"
699 print $2
700 return
701 fi
703 while [[ -n ${cur} ]];
704 do
705 cur=${cur%%*(/)*([!/])}
706 if [[ -z $ret ]]; then
707 ret=".."
708 else
709 ret="../$ret"
710 fi
711 done
712 print $ret
713 fi
714 }
717 #
718 # frame_nav_js
719 #
720 # Emit javascript for frame navigation
721 #
722 function frame_nav_js
723 {
724 cat << \EOF
725 var myInt;
726 var scrolling=0;
727 var sfactor = 3;
728 var scount=10;
730 function scrollByPix() {
731 if (scount<=0) {
732 sfactor*=1.2;
733 scount=10;
734 }
735 parent.lhs.scrollBy(0,sfactor);
736 parent.rhs.scrollBy(0,sfactor);
737 scount--;
738 }
740 function scrollToAnc(num) {
742 // Update the value of the anchor in the form which we use as
743 // storage for this value. setAncValue() will take care of
744 // correcting for overflow and underflow of the value and return
745 // us the new value.
746 num = setAncValue(num);
748 // Set location and scroll back a little to expose previous
749 // lines.
750 //
751 // Note that this could be improved: it is possible although
752 // complex to compute the x and y position of an anchor, and to
753 // scroll to that location directly.
754 //
755 parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
756 parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
758 parent.lhs.scrollBy(0,-30);
759 parent.rhs.scrollBy(0,-30);
760 }
762 function getAncValue()
763 {
764 return (parseInt(parent.nav.document.diff.real.value));
765 }
767 function setAncValue(val)
768 {
769 if (val <= 0) {
770 val = 0;
771 parent.nav.document.diff.real.value = val;
772 parent.nav.document.diff.display.value = "BOF";
773 return (val);
774 }
776 //
777 // The way we compute the max anchor value is to stash it
778 // inline in the left and right hand side pages-- it's the same
779 // on each side, so we pluck from the left.
780 //
781 maxval = parent.lhs.document.eof.value.value;
782 if (val < maxval) {
783 parent.nav.document.diff.real.value = val;
784 parent.nav.document.diff.display.value = val.toString();
785 return (val);
786 }
788 // this must be: val >= maxval
789 val = maxval;
790 parent.nav.document.diff.real.value = val;
791 parent.nav.document.diff.display.value = "EOF";
792 return (val);
793 }
795 function stopScroll() {
796 if (scrolling==1) {
797 clearInterval(myInt);
798 scrolling=0;
799 }
800 }
802 function startScroll() {
803 stopScroll();
804 scrolling=1;
805 myInt=setInterval("scrollByPix()",10);
806 }
808 function handlePress(b) {
810 switch (b) {
811 case 1 :
812 scrollToAnc(-1);
813 break;
814 case 2 :
815 scrollToAnc(getAncValue() - 1);
816 break;
817 case 3 :
818 sfactor=-3;
819 startScroll();
820 break;
821 case 4 :
822 sfactor=3;
823 startScroll();
824 break;
825 case 5 :
826 scrollToAnc(getAncValue() + 1);
827 break;
828 case 6 :
829 scrollToAnc(999999);
830 break;
831 }
832 }
834 function handleRelease(b) {
835 stopScroll();
836 }
838 function keypress(ev) {
839 var keynum;
840 var keychar;
842 if (window.event) { // IE
843 keynum = ev.keyCode;
844 } else if (ev.which) { // non-IE
845 keynum = ev.which;
846 }
848 keychar = String.fromCharCode(keynum);
850 if (keychar == "k") {
851 handlePress(2);
852 return (0);
853 } else if (keychar == "j" || keychar == " ") {
854 handlePress(5);
855 return (0);
856 }
857 return (1);
858 }
860 function ValidateDiffNum(){
861 val = parent.nav.document.diff.display.value;
862 if (val == "EOF") {
863 scrollToAnc(999999);
864 return;
865 }
867 if (val == "BOF") {
868 scrollToAnc(0);
869 return;
870 }
872 i=parseInt(val);
873 if (isNaN(i)) {
874 parent.nav.document.diff.display.value = getAncValue();
875 } else {
876 scrollToAnc(i);
877 }
878 return false;
879 }
881 EOF
882 }
884 #
885 # frame_navigation
886 #
887 # Output anchor navigation file for framed sdiffs.
888 #
889 function frame_navigation
890 {
891 print "$HTML<head>$STDHEAD"
893 cat << \EOF
894 <title>Anchor Navigation</title>
895 <meta http-equiv="Content-Script-Type" content="text/javascript" />
896 <meta http-equiv="Content-Type" content="text/html" />
898 <style type="text/css">
899 div.button td { padding-left: 5px; padding-right: 5px;
900 background-color: #eee; text-align: center;
901 border: 1px #444 outset; cursor: pointer; }
902 div.button a { font-weight: bold; color: black }
903 div.button td:hover { background: #ffcc99; }
904 </style>
905 EOF
907 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
909 cat << \EOF
910 </head>
911 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
912 onkeypress="keypress(event);">
913 <noscript lang="javascript">
914 <center>
915 <p><big>Framed Navigation controls require Javascript</big><br />
916 Either this browser is incompatable or javascript is not enabled</p>
917 </center>
918 </noscript>
919 <table width="100%" border="0" align="center">
920 <tr>
921 <td valign="middle" width="25%">Diff navigation:
922 Use 'j' and 'k' for next and previous diffs; or use buttons
923 at right</td>
924 <td align="center" valign="top" width="50%">
925 <div class="button">
926 <table border="0" align="center">
927 <tr>
928 <td>
929 <a onMouseDown="handlePress(1);return true;"
930 onMouseUp="handleRelease(1);return true;"
931 onMouseOut="handleRelease(1);return true;"
932 onClick="return false;"
933 title="Go to Beginning Of file">BOF</a></td>
934 <td>
935 <a onMouseDown="handlePress(3);return true;"
936 onMouseUp="handleRelease(3);return true;"
937 onMouseOut="handleRelease(3);return true;"
938 title="Scroll Up: Press and Hold to accelerate"
939 onClick="return false;">Scroll Up</a></td>
940 <td>
941 <a onMouseDown="handlePress(2);return true;"
942 onMouseUp="handleRelease(2);return true;"
943 onMouseOut="handleRelease(2);return true;"
944 title="Go to previous Diff"
945 onClick="return false;">Prev Diff</a>
946 </td></tr>
948 <tr>
949 <td>
950 <a onMouseDown="handlePress(6);return true;"
951 onMouseUp="handleRelease(6);return true;"
952 onMouseOut="handleRelease(6);return true;"
953 onClick="return false;"
954 title="Go to End Of File">EOF</a></td>
955 <td>
956 <a onMouseDown="handlePress(4);return true;"
957 onMouseUp="handleRelease(4);return true;"
958 onMouseOut="handleRelease(4);return true;"
959 title="Scroll Down: Press and Hold to accelerate"
960 onClick="return false;">Scroll Down</a></td>
961 <td>
962 <a onMouseDown="handlePress(5);return true;"
963 onMouseUp="handleRelease(5);return true;"
964 onMouseOut="handleRelease(5);return true;"
965 title="Go to next Diff"
966 onClick="return false;">Next Diff</a></td>
967 </tr>
968 </table>
969 </div>
970 </td>
971 <th valign="middle" width="25%">
972 <form action="" name="diff" onsubmit="return ValidateDiffNum();">
973 <input name="display" value="BOF" size="8" type="text" />
974 <input name="real" value="0" size="8" type="hidden" />
975 </form>
976 </th>
977 </tr>
978 </table>
979 </body>
980 </html>
981 EOF
982 }
986 #
987 # diff_to_html <filename> <filepath> { U | C } <comment>
988 #
989 # Processes the output of diff to produce an HTML file representing either
990 # context or unified diffs.
991 #
992 diff_to_html()
993 {
994 TNAME=$1
995 TPATH=$2
996 DIFFTYPE=$3
997 COMMENT=$4
999 print "$HTML<head>$STDHEAD"
1000 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
1002 if [[ $DIFFTYPE == "U" ]]; then
1003 print "$UDIFFCSS"
1004 fi
1006 cat <<-EOF
1007 </head>
1008 <body id="SUNWwebrev">
1009 <h2>$TPATH</h2>
1010 <a class="print" href="javascript:print()">Print this page</a>
1011 <pre>$COMMENT</pre>
1012 <pre>
1013 EOF
1015 html_quote | $AWK '
1016 /^--- new/ { next }
1017 /^\+\+\+ new/ { next }
1018 /^--- old/ { next }
1019 /^\*\*\* old/ { next }
1020 /^\*\*\*\*/ { next }
1021 /^-------/ { printf "<center><h1>%s</h1></center>\n", $0; next }
1022 /^\@\@.*\@\@$/ { printf "</pre><hr /><pre>\n";
1023 printf "<span class=\"newmarker\">%s</span>\n", $0;
1024 next}
1026 /^\*\*\*/ { printf "<hr /><span class=\"oldmarker\">%s</span>\n", $0;
1027 next}
1028 /^---/ { printf "<span class=\"newmarker\">%s</span>\n", $0;
1029 next}
1030 /^\+/ {printf "<span class=\"new\">%s</span>\n", $0; next}
1031 /^!/ {printf "<span class=\"changed\">%s</span>\n", $0; next}
1032 /^-/ {printf "<span class=\"removed\">%s</span>\n", $0; next}
1033 {printf "%s\n", $0; next}
1034 '
1036 print "</pre></body></html>\n"
1037 }
1040 #
1041 # source_to_html { new | old } <filename>
1042 #
1043 # Process a plain vanilla source file to transform it into an HTML file.
1044 #
1045 source_to_html()
1046 {
1047 WHICH=$1
1048 TNAME=$2
1050 print "$HTML<head>$STDHEAD"
1051 print "<title>$WHICH $TNAME</title>"
1052 print "<body id=\"SUNWwebrev\">"
1053 print "<pre>"
1054 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1055 print "</pre></body></html>"
1056 }
1058 #
1059 # teamwarecomments {text|html} parent-file child-file
1060 #
1061 # Find the first delta in the child that's not in the parent. Get the
1062 # newest delta from the parent, get all deltas from the child starting
1063 # with that delta, and then get all info starting with the second oldest
1064 # delta in that list (the first delta unique to the child).
1065 #
1066 # This code adapted from Bill Shannon's "spc" script
1067 #
1068 comments_from_teamware()
1069 {
1070 fmt=$1
1071 pfile=$PWS/$2
1072 cfile=$CWS/$3
1074 psid=$($SCCS prs -d:I: $pfile 2>/dev/null)
1075 if [[ -z "$psid" ]]; then
1076 psid=1.1
1077 fi
1079 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null)
1080 N=${#sids[@]}
1082 nawkprg='
1083 /^COMMENTS:/ {p=1; next}
1084 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
1085 NF == 0u { next }
1086 {if (p==0) next; print $0 }'
1088 if [[ $N -ge 2 ]]; then
1089 sid1=${sids[$((N-2))]} # Gets 2nd to last sid
1091 if [[ $fmt == "text" ]]; then
1092 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \
1093 $AWK "$nawkprg"
1094 return
1095 fi
1097 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \
1098 html_quote | bug2url | sac2url | $AWK "$nawkprg"
1099 fi
1100 }
1102 #
1103 # wxcomments {text|html} filepath
1104 #
1105 # Given the pathname of a file, find its location in a "wx" active file
1106 # list and print the following sccs comment. Output is either text or
1107 # HTML; if the latter, embedded bugids (sequence of 5 or more digits) are
1108 # turned into URLs.
1109 #
1110 comments_from_wx()
1111 {
1112 typeset fmt=$1
1113 typeset p=$2
1115 comm=`$AWK '
1116 $1 == "'$p'" {
1117 do getline ; while (NF > 0)
1118 getline
1119 while (NF > 0) { print ; getline }
1120 exit
1121 }' < $wxfile`
1123 if [[ $fmt == "text" ]]; then
1124 print "$comm"
1125 return
1126 fi
1128 print "$comm" | html_quote | bug2url | sac2url
1129 }
1131 comments_from_mercurial()
1132 {
1133 fmt=$1
1134 pfile=$PWS/$2
1135 cfile=$CWS/$3
1137 logdir=`dirname $cfile`
1138 logf=`basename $cfile`
1139 if [ -d $logdir ]; then
1140 ( cd $logdir;
1141 active=`hg status $logf 2>/dev/null`
1142 # If the output from 'hg status' is not empty, it means the file
1143 # hasn't been committed, so don't fetch comments.
1144 if [[ -z $active ]] ; then
1145 if [[ -n $ALL_CREV ]]; then
1146 rev_opt=
1147 for rev in $ALL_CREV; do
1148 rev_opt="$rev_opt --rev $rev"
1149 done
1150 comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf`
1151 elif [[ -n $FIRST_CREV ]]; then
1152 comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf`
1153 else
1154 comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf`
1155 fi
1156 else
1157 comm=""
1158 fi
1159 if [[ $fmt == "text" ]]; then
1160 print "$comm"
1161 return
1162 fi
1164 print "$comm" | html_quote | bug2url | sac2url
1165 )
1166 fi
1167 }
1170 #
1171 # getcomments {text|html} filepath parentpath
1172 #
1173 # Fetch the comments depending on what SCM mode we're in.
1174 #
1175 getcomments()
1176 {
1177 typeset fmt=$1
1178 typeset p=$2
1179 typeset pp=$3
1181 if [[ -n $wxfile ]]; then
1182 comments_from_wx $fmt $p
1183 else
1184 if [[ $SCM_MODE == "teamware" ]]; then
1185 comments_from_teamware $fmt $pp $p
1186 elif [[ $SCM_MODE == "mercurial" ]]; then
1187 comments_from_mercurial $fmt $pp $p
1188 fi
1189 fi
1190 }
1192 #
1193 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1194 #
1195 # Print out Code Inspection figures similar to sccs-prt(1) format.
1196 #
1197 function printCI
1198 {
1199 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1200 typeset str
1201 if (( tot == 1 )); then
1202 str="line"
1203 else
1204 str="lines"
1205 fi
1206 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \
1207 $tot $str $ins $del $mod $unc
1208 }
1211 #
1212 # difflines <oldfile> <newfile>
1213 #
1214 # Calculate and emit number of added, removed, modified and unchanged lines,
1215 # and total lines changed, the sum of added + removed + modified.
1216 #
1217 function difflines
1218 {
1219 integer tot mod del ins unc err
1220 typeset filename
1222 eval $( diff -e $1 $2 | $AWK '
1223 # Change range of lines: N,Nc
1224 /^[0-9]*,[0-9]*c$/ {
1225 n=split(substr($1,1,length($1)-1), counts, ",");
1226 if (n != 2) {
1227 error=2
1228 exit;
1229 }
1230 #
1231 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
1232 # following would be 5 - 3 = 2! Hence +1 for correction.
1233 #
1234 r=(counts[2]-counts[1])+1;
1236 #
1237 # Now count replacement lines: each represents a change instead
1238 # of a delete, so increment c and decrement r.
1239 #
1240 while (getline != /^\.$/) {
1241 c++;
1242 r--;
1243 }
1244 #
1245 # If there were more replacement lines than original lines,
1246 # then r will be negative; in this case there are no deletions,
1247 # but there are r changes that should be counted as adds, and
1248 # since r is negative, subtract it from a and add it to c.
1249 #
1250 if (r < 0) {
1251 a-=r;
1252 c+=r;
1253 }
1255 #
1256 # If there were more original lines than replacement lines, then
1257 # r will be positive; in this case, increment d by that much.
1258 #
1259 if (r > 0) {
1260 d+=r;
1261 }
1262 next;
1263 }
1265 # Change lines: Nc
1266 /^[0-9].*c$/ {
1267 # The first line is a replacement; any more are additions.
1268 if (getline != /^\.$/) {
1269 c++;
1270 while (getline != /^\.$/) a++;
1271 }
1272 next;
1273 }
1275 # Add lines: both Na and N,Na
1276 /^[0-9].*a$/ {
1277 while (getline != /^\.$/) a++;
1278 next;
1279 }
1281 # Delete range of lines: N,Nd
1282 /^[0-9]*,[0-9]*d$/ {
1283 n=split(substr($1,1,length($1)-1), counts, ",");
1284 if (n != 2) {
1285 error=2
1286 exit;
1287 }
1288 #
1289 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
1290 # following would be 5 - 3 = 2! Hence +1 for correction.
1291 #
1292 r=(counts[2]-counts[1])+1;
1293 d+=r;
1294 next;
1295 }
1297 # Delete line: Nd. For example 10d says line 10 is deleted.
1298 /^[0-9]*d$/ {d++; next}
1300 # Should not get here!
1301 {
1302 error=1;
1303 exit;
1304 }
1306 # Finish off - print results
1307 END {
1308 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
1309 (c+d+a), c, d, a, error);
1310 }' )
1312 # End of $AWK, Check to see if any trouble occurred.
1313 if (( $? > 0 || err > 0 )); then
1314 print "Unexpected Error occurred reading" \
1315 "\`diff -e $1 $2\`: \$?=$?, err=" $err
1316 return
1317 fi
1319 # Accumulate totals
1320 (( TOTL += tot ))
1321 (( TMOD += mod ))
1322 (( TDEL += del ))
1323 (( TINS += ins ))
1324 # Calculate unchanged lines
1325 unc=`wc -l < $1`
1326 if (( unc > 0 )); then
1327 (( unc -= del + mod ))
1328 (( TUNC += unc ))
1329 fi
1330 # print summary
1331 print "<span class=\"lineschanged\">\c"
1332 printCI $tot $ins $del $mod $unc
1333 print "</span>"
1334 }
1337 #
1338 # flist_from_wx
1339 #
1340 # Sets up webrev to source its information from a wx-formatted file.
1341 # Sets the global 'wxfile' variable.
1342 #
1343 function flist_from_wx
1344 {
1345 typeset argfile=$1
1346 if [[ -n ${argfile%%/*} ]]; then
1347 #
1348 # If the wx file pathname is relative then make it absolute
1349 # because the webrev does a "cd" later on.
1350 #
1351 wxfile=$PWD/$argfile
1352 else
1353 wxfile=$argfile
1354 fi
1356 $AWK '{ c = 1; print;
1357 while (getline) {
1358 if (NF == 0) { c = -c; continue }
1359 if (c > 0) print
1360 }
1361 }' $wxfile > $FLIST
1363 print " Done."
1364 }
1366 #
1367 # flist_from_teamware [ <args-to-putback-n> ]
1368 #
1369 # Generate the file list by extracting file names from a putback -n. Some
1370 # names may come from the "update/create" messages and others from the
1371 # "currently checked out" warning. Renames are detected here too. Extract
1372 # values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
1373 # -n as well, but remove them if they are already defined.
1374 #
1375 function flist_from_teamware
1376 {
1377 if [[ -n $codemgr_parent ]]; then
1378 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then
1379 print -u2 "parent $codemgr_parent doesn't look like a" \
1380 "valid teamware workspace"
1381 exit 1
1382 fi
1383 parent_args="-p $codemgr_parent"
1384 fi
1386 print " File list from: 'putback -n $parent_args $*' ... \c"
1388 putback -n $parent_args $* 2>&1 |
1389 $AWK '
1390 /^update:|^create:/ {print $2}
1391 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)}
1392 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)}
1393 /^The following files are currently checked out/ {p = 1; next}
1394 NF == 0 {p=0 ; next}
1395 /^rename/ {old=$3}
1396 $1 == "to:" {print $2, old}
1397 /^"/ {next}
1398 p == 1 {print $1}' |
1399 sort -r -k 1,1 -u | sort > $FLIST
1401 print " Done."
1402 }
1404 function outgoing_from_mercurial_forest
1405 {
1406 hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK '
1407 BEGIN {ntree=0}
1408 /^comparing/ {next}
1409 /^no changes/ {next}
1410 /^searching/ {next}
1411 /^\[.*\]$/ {tree=substr($1,2,length($1)-2);
1412 trees[ntree++] = tree;
1413 revs[tree]=-1;
1414 next}
1415 /^rev:/ {rev=$2+0;
1416 if (revs[tree] == -1 || rev < revs[tree])
1417 { revs[tree] = rev; };
1418 next;}
1419 END {for (tree in trees)
1420 { rev=revs[trees[tree]];
1421 if (rev > 0)
1422 {printf("%s %d\n",trees[tree],rev-1)}
1423 }}' | while read LINE
1424 do
1425 set - $LINE
1426 TREE=$1
1427 REV=$2
1428 A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'`
1429 FSTAT_OPT="--rev $A"
1430 print "Revision: $A $REV" >> $FLIST
1431 treestatus $TREE
1432 done
1433 }
1435 function flist_from_mercurial_forest
1436 {
1437 rm -f $FLIST
1438 if [ -z "$Nflag" ]; then
1439 print " File list from hg foutgoing $PWS ..."
1440 outgoing_from_mercurial_forest
1441 HG_LIST_FROM_COMMIT=1
1442 fi
1443 if [ ! -f $FLIST ]; then
1444 # hg commit hasn't been run see what is lying around
1445 print "\n No outgoing, perhaps you haven't commited."
1446 print " File list from hg fstatus -mard ...\c"
1447 FSTAT_OPT=
1448 fstatus
1449 HG_LIST_FROM_COMMIT=
1450 fi
1451 print " Done."
1452 }
1454 #
1455 # Used when dealing with the result of 'hg foutgoing'
1456 # When now go down the tree and generate the change list
1457 #
1458 function treestatus
1459 {
1460 TREE=$1
1461 HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
1463 $HGCMD -mdn 2>/dev/null | $FILTER | while read F
1464 do
1465 echo $TREE/$F
1466 done >> $FLIST
1468 # Then all the added files
1469 # But some of these could have been "moved" or renamed ones or copied ones
1470 # so let's make sure we get the proper info
1471 # hg status -aC will produce something like:
1472 # A subdir/File3
1473 # A subdir/File4
1474 # File4
1475 # A subdir/File5
1476 # The first and last are simple addition while the middle one
1477 # is a move/rename or a copy. We can't distinguish from a rename vs a copy
1478 # without also getting the status of removed files. The middle case above
1479 # is a rename if File4 is also shown a being removed. If File4 is not a
1480 # removed file, then the middle case is a copy from File4 to subdir/File4
1481 # FIXME - we're not distinguishing copy from rename
1482 $HGCMD -aC | $FILTER | while read LINE; do
1483 ldone=""
1484 while [ -z "$ldone" ]; do
1485 ldone="1"
1486 set - $LINE
1487 if [ $# -eq 2 -a "$1" == "A" ]; then
1488 AFILE=$2
1489 if read LINE2; then
1490 set - $LINE2
1491 if [ $# -eq 1 ]; then
1492 echo $TREE/$AFILE $TREE/$1 >>$FLIST
1493 elif [ $# -eq 2 ]; then
1494 echo $TREE/$AFILE >>$FLIST
1495 LINE=$LINE2
1496 ldone=""
1497 fi
1498 else
1499 echo $TREE/$AFILE >>$FLIST
1500 fi
1501 fi
1502 done
1503 done
1504 $HGCMD -rn | $FILTER | while read RFILE; do
1505 grep "$TREE/$RFILE" $FLIST >/dev/null
1506 if [ $? -eq 1 ]; then
1507 echo $TREE/$RFILE >>$FLIST
1508 fi
1509 done
1510 }
1512 function fstatus
1513 {
1514 #
1515 # forest extension is still being changed. For instance the output
1516 # of fstatus used to no prepend the tree path to filenames, but
1517 # this has changed recently. AWK code below does try to handle both
1518 # cases
1519 #
1520 hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1521 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1522 $1 != "" {n=index($1,tree);
1523 if (n == 0)
1524 { printf("%s/%s\n",tree,$1)}
1525 else
1526 { printf("%s\n",$1)}}' >> $FLIST
1528 #
1529 # There is a bug in the output of fstatus -aC on recent versions: it
1530 # inserts a space between the name of the tree and the filename of the
1531 # old file. e.g.:
1532 #
1533 # $ hg fstatus -aC
1534 # [.]
1535 #
1536 # [MyWS]
1537 # A MyWS/subdir/File2
1538 # MyWS/ File2
1539 #
1540 # [MyWS2]
1541 #
1543 hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1544 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1545 /^A .*/ {n=index($2,tree);
1546 if (n == 0)
1547 { printf("A %s/%s\n",tree,$2)}
1548 else
1549 { printf("A %s\n",$2)};
1550 next}
1551 /^ / {n=index($1,tree);
1552 if (n == 0)
1553 { printf("%s/%s\n",tree,$1)}
1554 else
1555 { if (NF == 2)
1556 printf("%s/%s\n",tree,$2)
1557 else
1558 printf("%s\n",$1)
1559 };
1560 next}
1561 ' | while read LINE; do
1562 ldone=""
1563 while [ -z "$ldone" ]; do
1564 ldone="1"
1565 set - $LINE
1566 if [ $# -eq 2 -a "$1" == "A" ]; then
1567 AFILE=$2
1568 if read LINE2; then
1569 set - $LINE2
1570 if [ $# -eq 1 ]; then
1571 echo $AFILE $1 >>$FLIST
1572 elif [ $# -eq 2 ]; then
1573 echo $AFILE >>$FLIST
1574 LINE=$LINE2
1575 ldone=""
1576 fi
1577 else
1578 echo $AFILE >>$FLIST
1579 fi
1580 fi
1581 done
1582 done
1583 hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1584 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1585 $1 != "" {n=index($1,tree);
1586 if (n == 0)
1587 { printf("%s/%s\n",tree,$1)}
1588 else
1589 { printf("%s\n",$1)}}' | while read RFILE; do
1590 grep "$RFILE" $FLIST >/dev/null
1591 if [ $? -eq 1 ]; then
1592 echo $RFILE >>$FLIST
1593 fi
1594 done
1595 }
1597 #
1598 # flist_from_mercurial $PWS
1599 #
1600 # Only local file based repositories are supported at present
1601 # since even though we can determine the list from the parent finding
1602 # the changes is harder.
1603 #
1604 # We first look for any outgoing files, this is for when the user has
1605 # run hg commit. If we don't find any then we look with hg status.
1606 #
1607 # We need at least one of default-push or default paths set in .hg/hgrc
1608 # If neither are set we don't know who to compare with.
1610 function flist_from_mercurial
1611 {
1612 # if [ "${PWS##ssh://}" != "$PWS" -o \
1613 # "${PWS##http://}" != "$PWS" -o \
1614 # "${PWS##https://}" != "$PWS" ]; then
1615 # print "Remote Mercurial repositories not currently supported."
1616 # print "Set default and/or default-push to a local repository"
1617 # exit
1618 # fi
1619 if [[ -n $forestflag ]]; then
1620 HG_LIST_FROM_COMMIT=
1621 flist_from_mercurial_forest
1622 else
1623 STATUS_REV=
1624 if [[ -n $rflag ]]; then
1625 STATUS_REV="--rev $PARENT_REV"
1626 elif [[ -n $OUTREV ]]; then
1627 STATUS_REV="--rev $OUTREV"
1628 else
1629 # hg commit hasn't been run see what is lying around
1630 print "\n No outgoing, perhaps you haven't commited."
1631 fi
1632 # First let's list all the modified or deleted files
1634 hg status $STATUS_REV -mdn | $FILTER > $FLIST
1636 # Then all the added files
1637 # But some of these could have been "moved" or renamed ones
1638 # so let's make sure we get the proper info
1639 # hg status -aC will produce something like:
1640 # A subdir/File3
1641 # A subdir/File4
1642 # File4
1643 # A subdir/File5
1644 # The first and last are simple addition while the middle one
1645 # is a move/rename or a copy. We can't distinguish from a rename vs a copy
1646 # without also getting the status of removed files. The middle case above
1647 # is a rename if File4 is also shown a being removed. If File4 is not a
1648 # removed file, then the middle case is a copy from File4 to subdir/File4
1649 # FIXME - we're not distinguishing copy from rename
1651 hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
1652 while read LINE; do
1653 ldone=""
1654 while [ -z "$ldone" ]; do
1655 ldone="1"
1656 set - $LINE
1657 if [ $# -eq 2 -a "$1" == "A" ]; then
1658 AFILE=$2
1659 if read LINE2; then
1660 set - $LINE2
1661 if [ $# -eq 1 ]; then
1662 echo $AFILE $1 >>$FLIST
1663 elif [ $# -eq 2 ]; then
1664 echo $AFILE >>$FLIST
1665 LINE=$LINE2
1666 ldone=""
1667 fi
1668 else
1669 echo $AFILE >>$FLIST
1670 fi
1671 fi
1672 done
1673 done < $FLIST.temp
1674 hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
1675 while read RFILE; do
1676 grep "$RFILE" $FLIST >/dev/null
1677 if [ $? -eq 1 ]; then
1678 echo $RFILE >>$FLIST
1679 fi
1680 done < $FLIST.temp
1681 rm -f $FLIST.temp
1682 fi
1683 }
1685 function env_from_flist
1686 {
1687 [[ -r $FLIST ]] || return
1689 #
1690 # Use "eval" to set env variables that are listed in the file
1691 # list. Then copy those into our local versions of those
1692 # variables if they have not been set already.
1693 #
1694 eval `sed -e "s/#.*$//" $FLIST | grep = `
1696 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
1698 #
1699 # Check to see if CODEMGR_PARENT is set in the flist file.
1700 #
1701 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
1702 codemgr_parent=$CODEMGR_PARENT
1703 }
1705 #
1706 # detect_scm
1707 #
1708 # We dynamically test the SCM type; this allows future extensions to
1709 # new SCM types
1710 #
1711 function detect_scm
1712 {
1713 #
1714 # If CODEMGR_WS is specified in the flist file, we assume teamware.
1715 #
1716 if [[ -r $FLIST ]]; then
1717 egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
1718 if [[ $? -eq 0 ]]; then
1719 print "teamware"
1720 return
1721 fi
1722 fi
1724 #
1725 # The presence of $CODEMGR_WS and a Codemgr_wsdata directory
1726 # is our clue that this is a teamware workspace.
1727 # Same if true if current directory has a Codemgr_wsdata sub-dir
1728 #
1729 if [[ -z "$CODEMGR_WS" ]]; then
1730 CODEMGR_WS=`workspace name 2>/dev/null`
1731 fi
1733 if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
1734 print "teamware"
1735 elif [[ -d $PWD/Codemgr_wsdata ]]; then
1736 print "teamware"
1737 elif hg root >/dev/null ; then
1738 print "mercurial"
1739 else
1740 print "unknown"
1741 fi
1742 }
1744 #
1745 # Extract the parent workspace from the Codemgr_wsdata/parent file
1746 #
1747 function parent_from_teamware
1748 {
1749 if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
1750 tail -1 "$1/Codemgr_wsdata/parent"
1751 fi
1752 }
1754 function look_for_prog
1755 {
1756 typeset path
1757 typeset ppath
1758 typeset progname=$1
1760 DEVTOOLS=
1761 OS=`uname`
1762 if [[ "$OS" == "SunOS" ]]; then
1763 DEVTOOLS="/java/devtools/`uname -p`/bin"
1764 elif [[ "$OS" == "Linux" ]]; then
1765 DEVTOOLS="/java/devtools/linux/bin"
1766 fi
1768 ppath=$PATH
1769 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1770 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
1771 ppath=$ppath:/opt/onbld/bin/`uname -p`
1772 ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
1774 PATH=$ppath prog=`whence $progname`
1775 if [[ -n $prog ]]; then
1776 print $prog
1777 fi
1778 }
1780 function build_old_new_teamware
1781 {
1782 # If the child's version doesn't exist then
1783 # get a readonly copy.
1785 if [[ ! -f $F && -f SCCS/s.$F ]]; then
1786 $SCCS get -s $F
1787 fi
1789 #
1790 # Snag new version of file.
1791 #
1792 rm -f $newdir/$DIR/$F
1793 cp $F $newdir/$DIR/$F
1795 #
1796 # Get the parent's version of the file. First see whether the
1797 # child's version is checked out and get the parent's version
1798 # with keywords expanded or unexpanded as appropriate.
1799 #
1800 if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
1801 -f $PWS/$PDIR/SCCS/p.$PF ]; then
1802 rm -f $olddir/$PDIR/$PF
1803 if [ -f SCCS/p.$F ]; then
1804 $SCCS get -s -p -k $PWS/$PDIR/$PF \
1805 > $olddir/$PDIR/$PF
1806 else
1807 $SCCS get -s -p $PWS/$PDIR/$PF \
1808 > $olddir/$PDIR/$PF
1809 fi
1810 else
1811 if [[ -f $PWS/$PDIR/$PF ]]; then
1812 # Parent is not a real workspace, but just a raw
1813 # directory tree - use the file that's there as
1814 # the old file.
1816 rm -f $olddir/$DIR/$F
1817 cp $PWS/$PDIR/$PF $olddir/$DIR/$F
1818 fi
1819 fi
1820 }
1822 #
1823 # Find the parent for $1
1824 #
1825 function find_outrev
1826 {
1827 crev=$1
1828 prev=`hg log -r $crev --template '{parents}\n'`
1829 if [[ -z "$prev" ]]
1830 then
1831 # No specific parent means previous changeset is parent
1832 prev=`expr $crev - 1`
1833 else
1834 # Format is either of the following two:
1835 # 546:7df6fcf1183b
1836 # 548:16f1915bb5cd 547:ffaa4e775815
1837 prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
1838 fi
1839 print $prev
1840 }
1842 function extract_ssh_infos
1843 {
1844 CMD=$1
1845 if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
1846 ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
1847 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
1848 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
1849 else
1850 ssh_user=
1851 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
1852 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
1853 fi
1855 }
1857 function build_old_new_mercurial
1858 {
1859 olddir=$1
1860 newdir=$2
1861 DIR=$3
1862 F=$4
1863 #
1864 # new version of the file.
1865 #
1866 rm -rf $newdir/$DIR/$F
1867 if [ -f $F ]; then
1868 cp $F $newdir/$DIR/$F
1869 fi
1871 #
1872 # Old version of the file.
1873 #
1874 rm -rf $olddir/$DIR/$F
1876 if [ -n "$PWS" ]; then
1877 if expr "$PWS" : 'ssh://' >/dev/null
1878 then
1879 extract_ssh_infos $PWS
1880 if [ -n "$ssh_user" ]; then
1881 parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
1882 else
1883 parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
1884 fi
1885 else
1886 parent="hg -R $PWS --cwd $PWS"
1887 fi
1888 else
1889 parent=""
1890 fi
1892 if [ -z "$rename" ]; then
1893 if [ -n "$rflag" ]; then
1894 parentrev=$PARENT_REV
1895 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1896 parentrev=$OUTREV
1897 else
1898 if [[ -n $HG_BRANCH ]]; then
1899 parentrev=$HG_BRANCH
1900 else
1901 parentrev="tip"
1902 fi
1903 fi
1905 if [ -n "$parentrev" ]; then
1906 if [ -z "$parent" ]; then
1907 hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
1908 else
1909 # when specifying a workspace we have to provide
1910 # the full path
1911 $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
1912 fi
1913 fi
1914 else
1915 # It's a rename (or a move), or a copy, so let's make sure we move
1916 # to the right directory first, then restore it once done
1917 current_dir=`pwd`
1918 cd $CWS/$PDIR
1919 if [ -n "$rflag" ]; then
1920 parentrev=$PARENT_REV
1921 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1922 parentrev=$OUTREV
1923 fi
1924 if [ -z "$parentrev" ]; then
1925 parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
1926 fi
1927 if [ -n "$parentrev" ]; then
1928 mkdir -p $olddir/$PDIR
1929 if [ -z "$parent" ]; then
1930 hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
1931 else
1932 $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
1933 fi
1934 fi
1935 cd $current_dir
1936 fi
1937 }
1939 function build_old_new
1940 {
1941 if [[ $SCM_MODE == "teamware" ]]; then
1942 build_old_new_teamware $@
1943 fi
1945 if [[ $SCM_MODE == "mercurial" ]]; then
1946 build_old_new_mercurial $@
1947 fi
1948 }
1951 #
1952 # Usage message.
1953 #
1954 function usage
1955 {
1956 print "Usage:\twebrev [common-options]
1957 webrev [common-options] ( <file> | - )
1958 webrev [common-options] -w <wx file>
1959 webrev [common-options] -l [arguments to 'putback']
1961 Options:
1962 -v: Print the version of this tool.
1963 -b: Do not ignore changes in the amount of white space.
1964 -c <CR#>: Include link to CR (aka bugid) in the main page.
1965 -O: Print bugids/arc cases suitable for OpenJDK.
1966 -i <filename>: Include <filename> in the index.html file.
1967 -o <outdir>: Output webrev to specified directory.
1968 -p <compare-against>: Use specified parent wkspc or basis for comparison
1969 -w <wxfile>: Use specified wx active file.
1970 -u <username>: Use that username instead of 'guessing' one.
1971 -m: Forces the use of Mercurial
1972 -t: Forces the use of Teamware
1974 Mercurial only options:
1975 -r rev: Compare against a specified revision
1976 -N: Skip 'hg outgoing', use only 'hg status'
1977 -f: Use the forest extension
1979 Environment:
1980 WDIR: Control the output directory.
1981 WEBREV_BUGURL: Control the URL prefix for bugids.
1982 WEBREV_SACURL: Control the URL prefix for ARC cases.
1984 SCM Environment:
1985 Teamware: CODEMGR_WS: Workspace location.
1986 Teamware: CODEMGR_PARENT: Parent workspace location.
1988 "
1990 exit 2
1991 }
1993 #
1994 #
1995 # Main program starts here
1996 #
1997 #
1998 LANG="C"
1999 LC_ALL="C"
2000 export LANG LC_ALL
2001 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2003 set +o noclobber
2005 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
2006 [[ -z $WX ]] && WX=`look_for_prog wx`
2007 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
2008 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
2009 [[ -z $PERL ]] && PERL=`look_for_prog perl`
2010 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
2011 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
2012 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
2013 [[ -z $AWK ]] && AWK=`look_for_prog awk`
2014 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
2015 [[ -z $JAR ]] && JAR=`look_for_prog jar`
2016 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
2017 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
2018 [[ -z $WGET ]] && WGET=`look_for_prog wget`
2020 if uname | grep CYGWIN >/dev/null
2021 then
2022 ISWIN=1
2023 # Under windows mercurial outputs '\' instead of '/'
2024 FILTER="tr '\\\\' '/'"
2025 else
2026 FILTER="cat"
2027 fi
2029 if [[ ! -x $PERL ]]; then
2030 print -u2 "Error: No perl interpreter found. Exiting."
2031 exit 1
2032 fi
2034 #
2035 # These aren't fatal, but we want to note them to the user.
2036 # We don't warn on the absence of 'wx' until later when we've
2037 # determined that we actually need to try to invoke it.
2038 #
2039 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
2040 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
2041 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
2043 # Declare global total counters.
2044 integer TOTL TINS TDEL TMOD TUNC
2046 flist_mode=
2047 flist_file=
2048 bflag=
2049 iflag=
2050 oflag=
2051 pflag=
2052 uflag=
2053 lflag=
2054 wflag=
2055 Oflag=
2056 rflag=
2057 Nflag=
2058 forestflag=
2059 while getopts "c:i:o:p:r:u:lmtwONvfb" opt
2060 do
2061 case $opt in
2062 b) bflag=1;;
2064 i) iflag=1
2065 INCLUDE_FILE=$OPTARG;;
2067 o) oflag=1
2068 WDIR=$OPTARG;;
2070 p) pflag=1
2071 codemgr_parent=$OPTARG;;
2073 u) uflag=1
2074 username=$OPTARG;;
2076 c) if [[ -z $CRID ]]; then
2077 CRID=$OPTARG
2078 else
2079 CRID="$CRID $OPTARG"
2080 fi;;
2082 m) SCM_MODE="mercurial";;
2084 t) SCM_MODE="teamware";;
2086 #
2087 # If -l has been specified, we need to abort further options
2088 # processing, because subsequent arguments are going to be
2089 # arguments to 'putback -n'.
2090 #
2091 l) lflag=1
2092 break;;
2094 w) wflag=1;;
2096 O) Oflag=1;;
2098 N) Nflag=1;;
2100 f) forestflag=1;;
2102 r) rflag=1
2103 PARENT_REV=$OPTARG;;
2105 v) print "$0 version: $WEBREV_UPDATED";;
2108 ?) usage;;
2109 esac
2110 done
2112 FLIST=/tmp/$$.flist
2113 HG_LIST_FROM_COMMIT=
2115 if [[ -n $wflag && -n $lflag ]]; then
2116 usage
2117 fi
2119 if [[ -n $forestflag && -n $rflag ]]; then
2120 print "The -r <rev> flag is incompatible with the use of forests"
2121 exit 2
2122 fi
2124 #
2125 # If this manually set as the parent, and it appears to be an earlier webrev,
2126 # then note that fact and set the parent to the raw_files/new subdirectory.
2127 #
2128 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
2129 parent_webrev="$codemgr_parent"
2130 codemgr_parent="$codemgr_parent/raw_files/new"
2131 fi
2133 if [[ -z $wflag && -z $lflag ]]; then
2134 shift $(($OPTIND - 1))
2136 if [[ $1 == "-" ]]; then
2137 cat > $FLIST
2138 flist_mode="stdin"
2139 flist_done=1
2140 shift
2141 elif [[ -n $1 ]]; then
2142 if [[ ! -r $1 ]]; then
2143 print -u2 "$1: no such file or not readable"
2144 usage
2145 fi
2146 cat $1 > $FLIST
2147 flist_mode="file"
2148 flist_file=$1
2149 flist_done=1
2150 shift
2151 else
2152 flist_mode="auto"
2153 fi
2154 fi
2156 #
2157 # Before we go on to further consider -l and -w, work out which SCM we think
2158 # is in use.
2159 #
2160 if [[ -z $SCM_MODE ]]; then
2161 SCM_MODE=`detect_scm $FLIST`
2162 fi
2163 if [[ $SCM_MODE == "unknown" ]]; then
2164 print -u2 "Unable to determine SCM type currently in use."
2165 print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
2166 print -u2 " the environment or in the file list."
2167 print -u2 "For mercurial: webrev runs 'hg root'."
2168 exit 1
2169 fi
2171 print -u2 " SCM detected: $SCM_MODE"
2174 if [[ $SCM_MODE == "mercurial" ]]; then
2175 #
2176 # determine Workspace and parent workspace paths
2177 #
2178 CWS=`hg root | $FILTER`
2179 if [[ -n $pflag && -z "$PWS" ]]; then
2180 OUTPWS=$codemgr_parent
2181 # Let's try to expand it if it's an alias defined in [paths]
2182 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
2183 if [[ -n $tmp ]]; then
2184 OUTPWS="$tmp"
2185 fi
2186 if [[ -n $rflag ]]; then
2187 if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
2188 PWS=$codemgr_parent
2189 else
2190 PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
2191 fi
2192 fi
2193 fi
2194 #
2195 # OUTPWS is the parent repository to use when using 'hg outgoing'
2196 #
2197 if [[ -z $Nflag ]]; then
2198 if [[ -n $forestflag ]]; then
2199 #
2200 # for forest we have to rely on properly set default and
2201 # default-push because they can be different from the top one.
2202 # unless of course it was explicitely speficied with -p
2203 if [[ -z $pflag ]]; then
2204 OUTPWS=
2205 fi
2206 else
2207 #
2208 # Unfortunately mercurial is bugged and doesn't handle
2209 # aliases correctly in 'hg path default'
2210 # So let's do it ourselves. Sigh...
2211 if [[ -z "$OUTPWS" ]]; then
2212 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
2213 fi
2214 # Still empty, means no default-push
2215 if [[ -z "$OUTPWS" ]]; then
2216 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
2217 fi
2218 # Let's try to expand it if it's an alias defined in [paths]
2219 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
2220 if [[ -n $tmp ]]; then
2221 OUTPWS="$tmp"
2222 fi
2223 fi
2224 fi
2225 #
2226 # OUTPWS may contain username:password, let's make sure we remove the
2227 # sensitive information before we print out anything in the HTML
2228 #
2229 OUTPWS2=$OUTPWS
2230 if [[ -n $OUTPWS ]]; then
2231 if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
2232 # Remove everything between '://' and '@'
2233 OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
2234 fi
2235 fi
2237 if [[ -z $HG_BRANCH ]]; then
2238 HG_BRANCH=`hg branch`
2239 if [ "$HG_BRANCH" == "default" ]; then
2240 #
2241 # 'default' means no particular branch, so let's cancel that
2242 #
2243 HG_BRANCH=
2244 fi
2245 fi
2247 if [[ -z $forestflag ]]; then
2248 if [[ -z $Nflag ]]; then
2249 #
2250 # If no "-N", always do "hg outgoing" against parent
2251 # repository to determine list of outgoing revisions.
2252 #
2253 ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
2254 if [[ -n $ALL_CREV ]]; then
2255 FIRST_CREV=`echo "$ALL_CREV" | head -1`
2256 #
2257 # If no "-r", choose revision to compare against by
2258 # finding the latest revision not in the outgoing list.
2259 #
2260 if [[ -z $rflag ]]; then
2261 OUTREV=`find_outrev "$FIRST_CREV"`
2262 if [[ -n $OUTREV ]]; then
2263 HG_LIST_FROM_COMMIT=1
2264 fi
2265 fi
2266 fi
2267 elif [[ -n $rflag ]]; then
2268 #
2269 # If skipping "hg outgoing" but still comparing against a
2270 # specific revision (not the tip), set revision for comment
2271 # accumulation.
2272 #
2273 FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
2274 FIRST_CREV=`expr $FIRST_CREV + 1`
2275 fi
2276 fi
2277 #Let's check if a merge is needed, if so, issue a warning
2278 PREV=`hg parent | grep '^tag:.*tip$'`
2279 if [[ -z $PREV ]]; then
2280 print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
2281 fi
2282 fi
2284 if [[ -n $lflag ]]; then
2285 #
2286 # If the -l flag is given instead of the name of a file list,
2287 # then generate the file list by extracting file names from a
2288 # putback -n.
2289 #
2290 shift $(($OPTIND - 1))
2291 if [[ $SCM_MODE == "teamware" ]]; then
2292 flist_from_teamware "$*"
2293 elif [[ $SCM_MODE == "mercurial" ]]; then
2294 flist_from_mercurial
2295 fi
2296 flist_done=1
2297 shift $#
2299 elif [[ -n $wflag ]]; then
2300 #
2301 # If the -w is given then assume the file list is in Bonwick's "wx"
2302 # command format, i.e. pathname lines alternating with SCCS comment
2303 # lines with blank lines as separators. Use the SCCS comments later
2304 # in building the index.html file.
2305 #
2306 shift $(($OPTIND - 1))
2307 wxfile=$1
2308 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2309 if [[ -r $CODEMGR_WS/wx/active ]]; then
2310 wxfile=$CODEMGR_WS/wx/active
2311 fi
2312 fi
2314 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
2315 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2317 print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2318 flist_from_wx $wxfile
2319 flist_done=1
2320 if [[ -n "$*" ]]; then
2321 shift
2322 fi
2323 elif [[ $flist_mode == "stdin" ]]; then
2324 print -u2 " File list from: standard input"
2325 elif [[ $flist_mode == "file" ]]; then
2326 print -u2 " File list from: $flist_file"
2327 fi
2329 if [[ $# -gt 0 ]]; then
2330 print -u2 "WARNING: unused arguments: $*"
2331 fi
2333 if [[ $SCM_MODE == "teamware" ]]; then
2334 #
2335 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
2336 # be set in a number of ways, in decreasing precedence:
2337 #
2338 # 1) on the command line (only for the parent)
2339 # 2) in the user environment
2340 # 3) in the flist
2341 # 4) automatically based on the workspace (only for the parent)
2342 #
2344 #
2345 # Here is case (2): the user environment
2346 #
2347 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
2348 [[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
2350 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
2351 print -u2 "$codemgr_ws: no such workspace"
2352 exit 1
2353 fi
2355 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
2356 codemgr_parent=$CODEMGR_PARENT
2358 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
2359 print -u2 "$codemgr_parent: no such directory"
2360 exit 1
2361 fi
2363 #
2364 # If we're in auto-detect mode and we haven't already gotten the file
2365 # list, then see if we can get it by probing for wx.
2366 #
2367 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
2368 if [[ ! -x $WX ]]; then
2369 print -u2 "WARNING: wx not found!"
2370 fi
2372 #
2373 # We need to use wx list -w so that we get renamed files, etc.
2374 # but only if a wx active file exists-- otherwise wx will
2375 # hang asking us to initialize our wx information.
2376 #
2377 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
2378 print -u2 " File list from: 'wx list -w' ... \c"
2379 $WX list -w > $FLIST
2380 $WX comments > /tmp/$$.wx_comments
2381 wxfile=/tmp/$$.wx_comments
2382 print -u2 "done"
2383 flist_done=1
2384 fi
2385 fi
2387 #
2388 # If by hook or by crook we've gotten a file list by now (perhaps
2389 # from the command line), eval it to extract environment variables from
2390 # it: This is step (3).
2391 #
2392 env_from_flist
2394 #
2395 # Continuing step (3): If we still have no file list, we'll try to get
2396 # it from teamware.
2397 #
2398 if [[ -z $flist_done ]]; then
2399 flist_from_teamware
2400 env_from_flist
2401 fi
2403 if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
2404 codemgr_ws=$PWD
2405 fi
2406 #
2407 # Observe true directory name of CODEMGR_WS, as used later in
2408 # webrev title.
2409 #
2410 if [[ -n $codemgr_ws ]]; then
2411 codemgr_ws=$(cd $codemgr_ws;print $PWD)
2412 fi
2414 if [[ -n $codemgr_parent ]]; then
2415 codemgr_parent=$(cd $codemgr_parent;print $PWD)
2416 fi
2418 #
2419 # (4) If we still don't have a value for codemgr_parent, get it
2420 # from workspace.
2421 #
2422 [[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
2423 [[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
2425 if [[ ! -d $codemgr_parent ]]; then
2426 print -u2 "$CODEMGR_PARENT: no such parent workspace"
2427 exit 1
2428 fi
2430 #
2431 # Reset CODEMGR_WS to make sure teamware commands are happy.
2432 #
2433 CODEMGR_WS=$codemgr_ws
2434 CWS=$codemgr_ws
2435 PWS=$codemgr_parent
2436 elif [[ $SCM_MODE == "mercurial" ]]; then
2437 if [[ -z $flist_done ]]; then
2438 flist_from_mercurial $PWS
2439 fi
2440 fi
2442 #
2443 # If the user didn't specify a -i option, check to see if there is a
2444 # webrev-info file in the workspace directory.
2445 #
2446 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2447 iflag=1
2448 INCLUDE_FILE="$CWS/webrev-info"
2449 fi
2451 if [[ -n $iflag ]]; then
2452 if [[ ! -r $INCLUDE_FILE ]]; then
2453 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
2454 "not readable."
2455 exit 1
2456 else
2457 #
2458 # $INCLUDE_FILE may be a relative path, and the script alters
2459 # PWD, so we just stash a copy in /tmp.
2460 #
2461 cp $INCLUDE_FILE /tmp/$$.include
2462 fi
2463 fi
2465 #
2466 # Output directory.
2467 #
2468 if [[ -z $WDIR ]]; then
2469 WDIR=$CWS/webrev
2470 else
2471 # If the output directory doesn't end with '/webrev' or '/webrev/'
2472 # then add '/webrev'. This is for backward compatibility
2473 if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
2474 then
2475 WDIR=$WDIR/webrev
2476 fi
2477 fi
2478 # WDIR=${WDIR:-$CWS/webrev}
2480 #
2481 # Name of the webrev, derived from the workspace name; in the
2482 # future this could potentially be an option.
2483 #
2484 # Let's keep what's after the last '/'
2485 WNAME=${CWS##*/}
2487 #
2488 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
2489 #
2490 if [ ${WDIR%%/*} ]; then
2491 if [[ -n $ISWIN ]]; then
2492 if [ ${WDIR%%[A-Za-z]:*} ]; then
2493 WDIR=$PWD/$WDIR
2494 fi
2495 else
2496 WDIR=$PWD/$WDIR
2497 fi
2498 fi
2500 if [[ ! -d $WDIR ]]; then
2501 mkdir -p $WDIR
2502 [[ $? != 0 ]] && exit 1
2503 fi
2505 #
2506 # Summarize what we're going to do.
2507 #
2508 print " Workspace: $CWS"
2509 if [[ -n $parent_webrev ]]; then
2510 print "Compare against: webrev at $parent_webrev"
2511 elif [[ -n $OUTPWS2 ]]; then
2512 print "Compare against: $OUTPWS2"
2513 fi
2514 if [[ -n $HG_BRANCH ]]; then
2515 print " Branch: $HG_BRANCH"
2516 fi
2517 if [[ -n $rflag ]]; then
2518 print "Compare against version: $PARENT_REV"
2519 fi
2520 [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE"
2521 print " Output to: $WDIR"
2523 #
2524 # Save the file list in the webrev dir
2525 #
2526 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
2528 #
2529 # Bug IDs will be replaced by a URL. Order of precedence
2530 # is: default location, $WEBREV_BUGURL, the -O flag.
2531 #
2532 BUGURL='https://jbs.oracle.com/bugs/browse/'
2533 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
2534 if [[ -n "$Oflag" ]]; then
2535 CRID=`echo $CRID | sed -e 's/JDK-//'`
2536 BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
2537 IDPREFIX=''
2538 else
2539 IDPREFIX='JDK-'
2540 fi
2543 #
2544 # Likewise, ARC cases will be replaced by a URL. Order of precedence
2545 # is: default, $WEBREV_SACURL, the -O flag.
2546 #
2547 # Note that -O also triggers different substitution behavior for
2548 # SACURL. See sac2url().
2549 #
2550 SACURL='http://sac.eng.sun.com'
2551 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
2552 [[ -n $Oflag ]] && \
2553 SACURL='http://www.opensolaris.org/os/community/arc/caselog'
2555 rm -f $WDIR/$WNAME.patch
2556 rm -f $WDIR/$WNAME.changeset
2557 rm -f $WDIR/$WNAME.ps
2558 rm -f $WDIR/$WNAME.pdf
2560 touch $WDIR/$WNAME.patch
2562 print " Output Files:"
2564 #
2565 # Clean up the file list: Remove comments, blank lines and env variables.
2566 #
2567 sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean
2568 FLIST=/tmp/$$.flist.clean
2570 #
2571 # Clean up residual raw files
2572 #
2573 if [ -d $WDIR/raw_files ]; then
2574 rm -rf $WDIR/raw_files 2>/dev/null
2575 fi
2577 #
2578 # Should we ignore changes in white spaces when generating diffs?
2579 #
2580 if [[ -n $bflag ]]; then
2581 DIFFOPTS="-t"
2582 else
2583 DIFFOPTS="-bt"
2584 fi
2585 #
2586 # First pass through the files: generate the per-file webrev HTML-files.
2587 #
2588 while read LINE
2589 do
2590 set - $LINE
2591 P=$1
2593 if [[ $1 == "Revision:" ]]; then
2594 OUTREV=$2
2595 continue
2596 fi
2597 #
2598 # Normally, each line in the file list is just a pathname of a
2599 # file that has been modified or created in the child. A file
2600 # that is renamed in the child workspace has two names on the
2601 # line: new name followed by the old name.
2602 #
2603 oldname=""
2604 oldpath=""
2605 rename=
2606 if [[ $# -eq 2 ]]; then
2607 PP=$2 # old filename
2608 oldname=" (was $PP)"
2609 oldpath="$PP"
2610 rename=1
2611 PDIR=${PP%/*}
2612 if [[ $PDIR == $PP ]]; then
2613 PDIR="." # File at root of workspace
2614 fi
2616 PF=${PP##*/}
2618 DIR=${P%/*}
2619 if [[ $DIR == $P ]]; then
2620 DIR="." # File at root of workspace
2621 fi
2623 F=${P##*/}
2624 else
2625 DIR=${P%/*}
2626 if [[ "$DIR" == "$P" ]]; then
2627 DIR="." # File at root of workspace
2628 fi
2630 F=${P##*/}
2632 PP=$P
2633 PDIR=$DIR
2634 PF=$F
2635 fi
2637 # Make the webrev directory if necessary as it may have been
2638 # removed because it was empty
2639 if [ ! -d $CWS/$DIR ]; then
2640 mkdir -p $CWS/$DIR
2641 fi
2643 COMM=`getcomments html $P $PP`
2645 print "\t$P$oldname\n\t\t\c"
2647 # Make the webrev mirror directory if necessary
2648 mkdir -p $WDIR/$DIR
2650 # cd to the directory so the names are short
2651 cd $CWS/$DIR
2653 #
2654 # If we're in OpenSolaris mode, we enforce a minor policy:
2655 # help to make sure the reviewer doesn't accidentally publish
2656 # source which is in usr/closed/*
2657 #
2658 if [[ -n $Oflag ]]; then
2659 pclosed=${P##usr/closed/}
2660 if [[ $pclosed != $P ]]; then
2661 print "*** Omitting closed source for OpenSolaris" \
2662 "mode review"
2663 continue
2664 fi
2665 fi
2667 #
2668 # We stash old and new files into parallel directories in /tmp
2669 # and do our diffs there. This makes it possible to generate
2670 # clean looking diffs which don't have absolute paths present.
2671 #
2672 olddir=$WDIR/raw_files/old
2673 newdir=$WDIR/raw_files/new
2674 mkdir -p $olddir
2675 mkdir -p $newdir
2676 mkdir -p $olddir/$PDIR
2677 mkdir -p $newdir/$DIR
2679 build_old_new $olddir $newdir $DIR $F
2681 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
2682 print "*** Error: file not in parent or child"
2683 continue
2684 fi
2686 cd $WDIR/raw_files
2687 ofile=old/$PDIR/$PF
2688 nfile=new/$DIR/$F
2690 mv_but_nodiff=
2691 cmp $ofile $nfile > /dev/null 2>&1
2692 if [[ $? == 0 && $rename == 1 ]]; then
2693 mv_but_nodiff=1
2694 fi
2696 #
2697 # Cleaning up
2698 #
2699 rm -f $WDIR/$DIR/$F.cdiff.html
2700 rm -f $WDIR/$DIR/$F.udiff.html
2701 rm -f $WDIR/$DIR/$F.wdiff.html
2702 rm -f $WDIR/$DIR/$F.sdiff.html
2703 rm -f $WDIR/$DIR/$F-.html
2704 rm -f $WDIR/$DIR/$F.html
2706 its_a_jar=
2707 if expr $F : '.*\.jar' >/dev/null; then
2708 its_a_jar=1
2709 # It's a JAR file, let's do it differntly
2710 if [[ -z $JAR ]]; then
2711 print "No access to jar, so can't produce diffs for jar files"
2712 else
2713 if [ -f $ofile ]; then
2714 $JAR -tvf $ofile >"$ofile".lst
2715 fi
2716 if [ -f $nfile ]; then
2717 $JAR -tvf $nfile >"$nfile".lst
2718 fi
2720 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2722 ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
2723 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2724 > $WDIR/$DIR/$F.cdiff.html
2725 print " cdiffs\c"
2727 ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
2728 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2729 > $WDIR/$DIR/$F.udiff.html
2731 print " udiffs\c"
2733 if [[ -x $WDIFF ]]; then
2734 $WDIFF -c "$COMM" \
2735 -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
2736 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2737 if [[ $? -eq 0 ]]; then
2738 print " wdiffs\c"
2739 else
2740 print " wdiffs[fail]\c"
2741 fi
2742 fi
2744 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2745 > $WDIR/$DIR/$F.sdiff.html
2746 print " sdiffs\c"
2748 print " frames\c"
2750 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2752 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
2754 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2755 # renamed file: may also have differences
2756 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
2757 elif [[ -f $nfile ]]; then
2758 # new file: count added lines
2759 difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
2760 elif [[ -f $ofile ]]; then
2761 # old file: count deleted lines
2762 difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
2763 fi
2764 fi
2765 else
2767 #
2768 # If we have old and new versions of the file then run the
2769 # appropriate diffs. This is complicated by a couple of factors:
2770 #
2771 # - renames must be handled specially: we emit a 'remove'
2772 # diff and an 'add' diff
2773 # - new files and deleted files must be handled specially
2774 # - Solaris patch(1m) can't cope with file creation
2775 # (and hence renames) as of this writing.
2776 # - To make matters worse, gnu patch doesn't interpret the
2777 # output of Solaris diff properly when it comes to
2778 # adds and deletes. We need to do some "cleansing"
2779 # transformations:
2780 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@
2781 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@
2782 #
2783 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
2784 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
2786 if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]];
2787 then
2788 # Only need to generate a patch file here if there are no commits in outgoing
2789 # or if we've specified a file list
2790 rm -f $WDIR/$DIR/$F.patch
2791 if [[ -z $rename ]]; then
2792 if [ ! -f $ofile ]; then
2793 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2794 > $WDIR/$DIR/$F.patch
2795 elif [ ! -f $nfile ]; then
2796 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2797 > $WDIR/$DIR/$F.patch
2798 else
2799 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
2800 fi
2801 else
2802 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2803 > $WDIR/$DIR/$F.patch
2805 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2806 >> $WDIR/$DIR/$F.patch
2808 fi
2811 #
2812 # Tack the patch we just made onto the accumulated patch for the
2813 # whole wad.
2814 #
2815 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
2816 fi
2818 print " patch\c"
2820 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2822 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
2823 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2824 > $WDIR/$DIR/$F.cdiff.html
2825 print " cdiffs\c"
2827 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
2828 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2829 > $WDIR/$DIR/$F.udiff.html
2831 print " udiffs\c"
2833 if [[ -x $WDIFF ]]; then
2834 $WDIFF -c "$COMM" \
2835 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
2836 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2837 if [[ $? -eq 0 ]]; then
2838 print " wdiffs\c"
2839 else
2840 print " wdiffs[fail]\c"
2841 fi
2842 fi
2844 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2845 > $WDIR/$DIR/$F.sdiff.html
2846 print " sdiffs\c"
2848 print " frames\c"
2850 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2852 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2854 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2855 # renamed file: may also have differences
2856 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2857 elif [[ -f $nfile ]]; then
2858 # new file: count added lines
2859 difflines /dev/null $nfile > $WDIR/$DIR/$F.count
2860 elif [[ -f $ofile ]]; then
2861 # old file: count deleted lines
2862 difflines $ofile /dev/null > $WDIR/$DIR/$F.count
2863 fi
2864 fi
2865 #
2866 # Now we generate the postscript for this file. We generate diffs
2867 # only in the event that there is delta, or the file is new (it seems
2868 # tree-killing to print out the contents of deleted files).
2869 #
2870 if [[ -f $nfile ]]; then
2871 ocr=$ofile
2872 [[ ! -f $ofile ]] && ocr=/dev/null
2874 if [[ -z $mv_but_nodiff ]]; then
2875 textcomm=`getcomments text $P $PP`
2876 if [[ -x $CODEREVIEW ]]; then
2877 $CODEREVIEW -y "$textcomm" \
2878 -e $ocr $nfile \
2879 > /tmp/$$.psfile 2>/dev/null &&
2880 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
2881 if [[ $? -eq 0 ]]; then
2882 print " ps\c"
2883 else
2884 print " ps[fail]\c"
2885 fi
2886 fi
2887 fi
2888 fi
2890 if [[ -f $ofile && -z $mv_but_nodiff ]]; then
2891 if [[ -n $its_a_jar ]]; then
2892 source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
2893 else
2894 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
2895 fi
2896 print " old\c"
2897 fi
2899 if [[ -f $nfile ]]; then
2900 if [[ -n $its_a_jar ]]; then
2901 source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
2902 else
2903 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
2904 fi
2905 print " new\c"
2906 fi
2908 print
2909 done < $FLIST
2911 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
2912 if [[ $SCM_MODE == "mercurial" ]]; then
2913 if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then
2914 EXPORTCHANGESET="$WNAME.changeset"
2915 CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
2916 rm -f $CHANGESETPATH
2917 touch $CHANGESETPATH
2918 if [[ -n $ALL_CREV ]]; then
2919 rev_opt=
2920 for rev in $ALL_CREV; do
2921 rev_opt="$rev_opt --rev $rev"
2922 done
2923 elif [[ -n $FIRST_CREV ]]; then
2924 rev_opt="--rev $FIRST_CREV"
2925 fi
2927 if [[ -n $rev_opt ]]; then
2928 (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
2929 echo "Created changeset: $CHANGESETPATH" 1>&2
2930 # Use it in place of the jdk.patch created above
2931 rm -f $WDIR/$WNAME.patch
2932 fi
2933 set +x
2934 fi
2935 fi
2937 frame_nav_js > $WDIR/ancnav.js
2938 frame_navigation > $WDIR/ancnav.html
2940 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
2941 print " Generating PDF: \c"
2942 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
2943 print "Done."
2944 fi
2946 # Now build the index.html file that contains
2947 # links to the source files and their diffs.
2949 cd $CWS
2951 # Save total changed lines for Code Inspection.
2952 print "$TOTL" > $WDIR/TotalChangedLines
2954 print " index.html: \c"
2955 INDEXFILE=$WDIR/index.html
2956 exec 3<&1 # duplicate stdout to FD3.
2957 exec 1<&- # Close stdout.
2958 exec > $INDEXFILE # Open stdout to index file.
2960 print "$HTML<head>"
2961 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
2962 print "$STDHEAD"
2963 print "<title>$WNAME</title>"
2964 print "</head>"
2965 print "<body id=\"SUNWwebrev\">"
2966 print "<div class=\"summary\">"
2967 print "<h2>Code Review for $WNAME</h2>"
2969 print "<table>"
2971 if [[ -z $uflag ]]
2972 then
2973 if [[ $SCM_MODE == "mercurial" ]]
2974 then
2975 #
2976 # Let's try to extract the user name from the .hgrc file
2977 #
2978 username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
2979 fi
2981 if [[ -z $username ]]
2982 then
2983 #
2984 # Figure out the username and gcos name. To maintain compatibility
2985 # with passwd(4), we must support '&' substitutions.
2986 #
2987 username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
2988 if [[ -x $GETENT ]]; then
2989 realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
2990 fi
2991 userupper=`print "$username" | sed 's/\<./\u&/g'`
2992 realname=`print $realname | sed s/\&/$userupper/`
2993 fi
2994 fi
2996 date="on `date`"
2998 if [[ -n "$username" && -n "$realname" ]]; then
2999 print "<tr><th>Prepared by:</th>"
3000 print "<td>$realname ($username) $date</td></tr>"
3001 elif [[ -n "$username" ]]; then
3002 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
3003 fi
3005 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
3006 if [[ -n $parent_webrev ]]; then
3007 print "<tr><th>Compare against:</th><td>"
3008 print "webrev at $parent_webrev"
3009 else
3010 if [[ -n $OUTPWS2 ]]; then
3011 print "<tr><th>Compare against:</th><td>"
3012 print "$OUTPWS2"
3013 fi
3014 fi
3015 print "</td></tr>"
3016 if [[ -n $rflag ]]; then
3017 print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
3018 elif [[ -n $OUTREV ]]; then
3019 if [[ -z $forestflag ]]; then
3020 print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
3021 fi
3022 fi
3023 if [[ -n $HG_BRANCH ]]; then
3024 print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
3025 fi
3027 print "<tr><th>Summary of changes:</th><td>"
3028 printCI $TOTL $TINS $TDEL $TMOD $TUNC
3029 print "</td></tr>"
3031 if [[ -f $WDIR/$WNAME.patch ]]; then
3032 print "<tr><th>Patch of changes:</th><td>"
3033 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
3034 elif [[ -f $CHANGESETPATH ]]; then
3035 print "<tr><th>Changeset:</th><td>"
3036 print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
3037 fi
3039 if [[ -f $WDIR/$WNAME.pdf ]]; then
3040 print "<tr><th>Printable review:</th><td>"
3041 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
3042 fi
3044 if [[ -n "$iflag" ]]; then
3045 print "<tr><th>Author comments:</th><td><div>"
3046 cat /tmp/$$.include
3047 print "</div></td></tr>"
3048 fi
3049 # Add links to referenced CRs, if any
3050 # external URL has a <title> like:
3051 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
3052 # while internal URL has <title> like:
3053 # <title>[#JDK-6641309] Wrong Cookie separator used in HttpURLConnection</title>
3054 #
3055 if [[ -n $CRID ]]; then
3056 for id in $CRID
3057 do
3058 if [[ -z "$Oflag" ]]; then
3059 #add "JDK-" to raw bug id for jbs links.
3060 id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
3061 fi
3062 print "<tr><th>Bug id:</th><td>"
3063 url="${BUGURL}${id}"
3064 if [[ -n "$Oflag" ]]; then
3065 cleanup='s/Bug ID: \([0-9]\{5,\}\) \(.*\)/JDK-\1 : \2/'
3066 else
3067 cleanup='s|\[#\(JDK-[0-9]\{5,\}\)\] \(.*\)|\1 : \2|'
3068 fi
3069 if [[ -n $WGET ]]; then
3070 msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed "$cleanup" | html_quote`
3071 fi
3072 if [[ -z $msg ]]; then
3073 msg="${id}"
3074 fi
3076 print "<a href=\"$url\">$msg</a>"
3078 print "</td></tr>"
3079 done
3080 fi
3081 print "<tr><th>Legend:</th><td>"
3082 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>"
3083 print "</table>"
3084 print "</div>"
3086 #
3087 # Second pass through the files: generate the rest of the index file
3088 #
3089 while read LINE
3090 do
3091 set - $LINE
3092 if [[ $1 == "Revision:" ]]; then
3093 FIRST_CREV=`expr $3 + 1`
3094 continue
3095 fi
3096 P=$1
3098 if [[ $# == 2 ]]; then
3099 PP=$2
3100 oldname=" <i>(was $PP)</i>"
3102 else
3103 PP=$P
3104 oldname=""
3105 fi
3107 DIR=${P%/*}
3108 if [[ $DIR == $P ]]; then
3109 DIR="." # File at root of workspace
3110 fi
3112 # Avoid processing the same file twice.
3113 # It's possible for renamed files to
3114 # appear twice in the file list
3116 F=$WDIR/$P
3118 print "<p><code>"
3120 # If there's a diffs file, make diffs links
3122 NODIFFS=
3123 if [[ -f $F.cdiff.html ]]; then
3124 print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
3125 print "<a href=\"$P.udiff.html\">Udiffs</a>"
3127 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
3128 print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
3129 fi
3131 print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
3133 print "<a href=\"$P.frames.html\">Frames</a>"
3134 else
3135 NODIFFS=1
3136 print " ------ ------ ------"
3138 if [[ -x $WDIFF ]]; then
3139 print " ------"
3140 fi
3142 print " ------"
3143 fi
3145 # If there's an old file, make the link
3147 NOOLD=
3148 if [[ -f $F-.html ]]; then
3149 print "<a href=\"$P-.html\">Old</a>"
3150 else
3151 NOOLD=1
3152 print " ---"
3153 fi
3155 # If there's an new file, make the link
3157 NONEW=
3158 if [[ -f $F.html ]]; then
3159 print "<a href=\"$P.html\">New</a>"
3160 else
3161 NONEW=1
3162 print " ---"
3163 fi
3165 if [[ -f $F.patch ]]; then
3166 print "<a href=\"$P.patch\">Patch</a>"
3167 else
3168 print " -----"
3169 fi
3171 if [[ -f $WDIR/raw_files/new/$P ]]; then
3172 print "<a href=\"raw_files/new/$P\">Raw</a>"
3173 else
3174 print " ---"
3175 fi
3176 print "</code>"
3177 if [[ -n $NODIFFS && -z $oldname ]]; then
3178 if [[ -n $NOOLD ]]; then
3179 print "<font color=green><b>$P</b></font>"
3180 elif [[ -n $NONEW ]]; then
3181 print "<font color=red><b>$P</b></font>"
3182 fi
3183 else
3184 print "<b>$P</b> $oldname"
3185 fi
3187 #
3188 # Check for usr/closed
3189 #
3190 if [ ! -z "$Oflag" ]; then
3191 if [[ $P == usr/closed/* ]]; then
3192 print " <i>Closed source: omitted from" \
3193 "this review</i>"
3194 fi
3195 fi
3197 print "</p><blockquote>\c"
3198 # Insert delta comments if any
3199 comments=`getcomments html $P $PP`
3200 if [ -n "$comments" ]; then
3201 print "<pre>$comments</pre>"
3202 fi
3204 # Add additional comments comment
3206 print "<!-- Add comments to explain changes in $P here -->"
3208 # Add count of changes.
3210 if [[ -f $F.count ]]; then
3211 cat $F.count
3212 rm $F.count
3213 fi
3214 print "</blockquote>"
3215 done < $FLIST
3217 print
3218 print
3219 print "<hr />"
3220 print "<p style=\"font-size: small\">"
3221 print "This code review page was prepared using <b>$0</b>"
3222 print "(vers $WEBREV_UPDATED)."
3223 print "</body>"
3224 print "</html>"
3226 if [[ -n $ZIP ]]; then
3227 # Let's generate a zip file for convenience
3228 cd $WDIR/..
3229 if [ -f webrev.zip ]; then
3230 rm webrev.zip
3231 fi
3232 $ZIP -r webrev webrev >/dev/null 2>&1
3233 fi
3235 exec 1<&- # Close FD 1.
3236 exec 1<&3 # dup FD 3 to restore stdout.
3237 exec 3<&- # close FD 3.
3239 print "Done."
3240 print "Output to: $WDIR"