Tue, 26 Mar 2013 13:41:36 -0400
8009824: webrev.ksh generated jdk.patch files do not handle renames, copies, and shouldn't be applied
Summary: use hg export --git to produce proper patch file
Reviewed-by: mduigou
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=23.18-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 NO_OUTGOING=
1447 print " File list from hg fstatus -mard ...\c"
1448 FSTAT_OPT=
1449 fstatus
1450 HG_LIST_FROM_COMMIT=0
1451 fi
1452 print " Done."
1453 }
1455 #
1456 # Used when dealing with the result of 'hg foutgoing'
1457 # When now go down the tree and generate the change list
1458 #
1459 function treestatus
1460 {
1461 TREE=$1
1462 HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
1464 $HGCMD -mdn 2>/dev/null | $FILTER | while read F
1465 do
1466 echo $TREE/$F
1467 done >> $FLIST
1469 # Then all the added files
1470 # But some of these could have been "moved" or renamed ones or copied ones
1471 # so let's make sure we get the proper info
1472 # hg status -aC will produce something like:
1473 # A subdir/File3
1474 # A subdir/File4
1475 # File4
1476 # A subdir/File5
1477 # The first and last are simple addition while the middle one
1478 # is a move/rename or a copy. We can't distinguish from a rename vs a copy
1479 # without also getting the status of removed files. The middle case above
1480 # is a rename if File4 is also shown a being removed. If File4 is not a
1481 # removed file, then the middle case is a copy from File4 to subdir/File4
1482 # FIXME - we're not distinguishing copy from rename
1483 $HGCMD -aC | $FILTER | while read LINE; do
1484 ldone=""
1485 while [ -z "$ldone" ]; do
1486 ldone="1"
1487 set - $LINE
1488 if [ $# -eq 2 -a "$1" == "A" ]; then
1489 AFILE=$2
1490 if read LINE2; then
1491 set - $LINE2
1492 if [ $# -eq 1 ]; then
1493 echo $TREE/$AFILE $TREE/$1 >>$FLIST
1494 elif [ $# -eq 2 ]; then
1495 echo $TREE/$AFILE >>$FLIST
1496 LINE=$LINE2
1497 ldone=""
1498 fi
1499 else
1500 echo $TREE/$AFILE >>$FLIST
1501 fi
1502 fi
1503 done
1504 done
1505 $HGCMD -rn | $FILTER | while read RFILE; do
1506 grep "$TREE/$RFILE" $FLIST >/dev/null
1507 if [ $? -eq 1 ]; then
1508 echo $TREE/$RFILE >>$FLIST
1509 fi
1510 done
1511 }
1513 function fstatus
1514 {
1515 #
1516 # forest extension is still being changed. For instance the output
1517 # of fstatus used to no prepend the tree path to filenames, but
1518 # this has changed recently. AWK code below does try to handle both
1519 # cases
1520 #
1521 hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1522 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1523 $1 != "" {n=index($1,tree);
1524 if (n == 0)
1525 { printf("%s/%s\n",tree,$1)}
1526 else
1527 { printf("%s\n",$1)}}' >> $FLIST
1529 #
1530 # There is a bug in the output of fstatus -aC on recent versions: it
1531 # inserts a space between the name of the tree and the filename of the
1532 # old file. e.g.:
1533 #
1534 # $ hg fstatus -aC
1535 # [.]
1536 #
1537 # [MyWS]
1538 # A MyWS/subdir/File2
1539 # MyWS/ File2
1540 #
1541 # [MyWS2]
1542 #
1544 hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1545 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1546 /^A .*/ {n=index($2,tree);
1547 if (n == 0)
1548 { printf("A %s/%s\n",tree,$2)}
1549 else
1550 { printf("A %s\n",$2)};
1551 next}
1552 /^ / {n=index($1,tree);
1553 if (n == 0)
1554 { printf("%s/%s\n",tree,$1)}
1555 else
1556 { if (NF == 2)
1557 printf("%s/%s\n",tree,$2)
1558 else
1559 printf("%s\n",$1)
1560 };
1561 next}
1562 ' | while read LINE; do
1563 ldone=""
1564 while [ -z "$ldone" ]; do
1565 ldone="1"
1566 set - $LINE
1567 if [ $# -eq 2 -a "$1" == "A" ]; then
1568 AFILE=$2
1569 if read LINE2; then
1570 set - $LINE2
1571 if [ $# -eq 1 ]; then
1572 echo $AFILE $1 >>$FLIST
1573 elif [ $# -eq 2 ]; then
1574 echo $AFILE >>$FLIST
1575 LINE=$LINE2
1576 ldone=""
1577 fi
1578 else
1579 echo $AFILE >>$FLIST
1580 fi
1581 fi
1582 done
1583 done
1584 hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1585 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1586 $1 != "" {n=index($1,tree);
1587 if (n == 0)
1588 { printf("%s/%s\n",tree,$1)}
1589 else
1590 { printf("%s\n",$1)}}' | while read RFILE; do
1591 grep "$RFILE" $FLIST >/dev/null
1592 if [ $? -eq 1 ]; then
1593 echo $RFILE >>$FLIST
1594 fi
1595 done
1596 }
1598 #
1599 # flist_from_mercurial $PWS
1600 #
1601 # Only local file based repositories are supported at present
1602 # since even though we can determine the list from the parent finding
1603 # the changes is harder.
1604 #
1605 # We first look for any outgoing files, this is for when the user has
1606 # run hg commit. If we don't find any then we look with hg status.
1607 #
1608 # We need at least one of default-push or default paths set in .hg/hgrc
1609 # If neither are set we don't know who to compare with.
1611 function flist_from_mercurial
1612 {
1613 # if [ "${PWS##ssh://}" != "$PWS" -o \
1614 # "${PWS##http://}" != "$PWS" -o \
1615 # "${PWS##https://}" != "$PWS" ]; then
1616 # print "Remote Mercurial repositories not currently supported."
1617 # print "Set default and/or default-push to a local repository"
1618 # exit
1619 # fi
1620 if [[ -n $forestflag ]]; then
1621 HG_LIST_FROM_COMMIT=
1622 flist_from_mercurial_forest
1623 else
1624 STATUS_REV=
1625 if [[ -n $rflag ]]; then
1626 STATUS_REV="--rev $PARENT_REV"
1627 elif [[ -n $OUTREV ]]; then
1628 STATUS_REV="--rev $OUTREV"
1629 else
1630 # hg commit hasn't been run see what is lying around
1631 print "\n No outgoing, perhaps you haven't commited."
1632 NO_OUTGOING=
1633 fi
1634 # First let's list all the modified or deleted files
1636 hg status $STATUS_REV -mdn | $FILTER > $FLIST
1638 # Then all the added files
1639 # But some of these could have been "moved" or renamed ones
1640 # so let's make sure we get the proper info
1641 # hg status -aC will produce something like:
1642 # A subdir/File3
1643 # A subdir/File4
1644 # File4
1645 # A subdir/File5
1646 # The first and last are simple addition while the middle one
1647 # is a move/rename or a copy. We can't distinguish from a rename vs a copy
1648 # without also getting the status of removed files. The middle case above
1649 # is a rename if File4 is also shown a being removed. If File4 is not a
1650 # removed file, then the middle case is a copy from File4 to subdir/File4
1651 # FIXME - we're not distinguishing copy from rename
1653 hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
1654 while read LINE; do
1655 ldone=""
1656 while [ -z "$ldone" ]; do
1657 ldone="1"
1658 set - $LINE
1659 if [ $# -eq 2 -a "$1" == "A" ]; then
1660 AFILE=$2
1661 if read LINE2; then
1662 set - $LINE2
1663 if [ $# -eq 1 ]; then
1664 echo $AFILE $1 >>$FLIST
1665 elif [ $# -eq 2 ]; then
1666 echo $AFILE >>$FLIST
1667 LINE=$LINE2
1668 ldone=""
1669 fi
1670 else
1671 echo $AFILE >>$FLIST
1672 fi
1673 fi
1674 done
1675 done < $FLIST.temp
1676 hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
1677 while read RFILE; do
1678 grep "$RFILE" $FLIST >/dev/null
1679 if [ $? -eq 1 ]; then
1680 echo $RFILE >>$FLIST
1681 fi
1682 done < $FLIST.temp
1683 rm -f $FLIST.temp
1684 fi
1685 }
1687 function env_from_flist
1688 {
1689 [[ -r $FLIST ]] || return
1691 #
1692 # Use "eval" to set env variables that are listed in the file
1693 # list. Then copy those into our local versions of those
1694 # variables if they have not been set already.
1695 #
1696 eval `sed -e "s/#.*$//" $FLIST | grep = `
1698 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
1700 #
1701 # Check to see if CODEMGR_PARENT is set in the flist file.
1702 #
1703 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
1704 codemgr_parent=$CODEMGR_PARENT
1705 }
1707 #
1708 # detect_scm
1709 #
1710 # We dynamically test the SCM type; this allows future extensions to
1711 # new SCM types
1712 #
1713 function detect_scm
1714 {
1715 #
1716 # If CODEMGR_WS is specified in the flist file, we assume teamware.
1717 #
1718 if [[ -r $FLIST ]]; then
1719 egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
1720 if [[ $? -eq 0 ]]; then
1721 print "teamware"
1722 return
1723 fi
1724 fi
1726 #
1727 # The presence of $CODEMGR_WS and a Codemgr_wsdata directory
1728 # is our clue that this is a teamware workspace.
1729 # Same if true if current directory has a Codemgr_wsdata sub-dir
1730 #
1731 if [[ -z "$CODEMGR_WS" ]]; then
1732 CODEMGR_WS=`workspace name 2>/dev/null`
1733 fi
1735 if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
1736 print "teamware"
1737 elif [[ -d $PWD/Codemgr_wsdata ]]; then
1738 print "teamware"
1739 elif hg root >/dev/null ; then
1740 print "mercurial"
1741 else
1742 print "unknown"
1743 fi
1744 }
1746 #
1747 # Extract the parent workspace from the Codemgr_wsdata/parent file
1748 #
1749 function parent_from_teamware
1750 {
1751 if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
1752 tail -1 "$1/Codemgr_wsdata/parent"
1753 fi
1754 }
1756 function look_for_prog
1757 {
1758 typeset path
1759 typeset ppath
1760 typeset progname=$1
1762 DEVTOOLS=
1763 OS=`uname`
1764 if [[ "$OS" == "SunOS" ]]; then
1765 DEVTOOLS="/java/devtools/`uname -p`/bin"
1766 elif [[ "$OS" == "Linux" ]]; then
1767 DEVTOOLS="/java/devtools/linux/bin"
1768 fi
1770 ppath=$PATH
1771 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1772 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
1773 ppath=$ppath:/opt/onbld/bin/`uname -p`
1774 ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
1776 PATH=$ppath prog=`whence $progname`
1777 if [[ -n $prog ]]; then
1778 print $prog
1779 fi
1780 }
1782 function build_old_new_teamware
1783 {
1784 # If the child's version doesn't exist then
1785 # get a readonly copy.
1787 if [[ ! -f $F && -f SCCS/s.$F ]]; then
1788 $SCCS get -s $F
1789 fi
1791 #
1792 # Snag new version of file.
1793 #
1794 rm -f $newdir/$DIR/$F
1795 cp $F $newdir/$DIR/$F
1797 #
1798 # Get the parent's version of the file. First see whether the
1799 # child's version is checked out and get the parent's version
1800 # with keywords expanded or unexpanded as appropriate.
1801 #
1802 if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
1803 -f $PWS/$PDIR/SCCS/p.$PF ]; then
1804 rm -f $olddir/$PDIR/$PF
1805 if [ -f SCCS/p.$F ]; then
1806 $SCCS get -s -p -k $PWS/$PDIR/$PF \
1807 > $olddir/$PDIR/$PF
1808 else
1809 $SCCS get -s -p $PWS/$PDIR/$PF \
1810 > $olddir/$PDIR/$PF
1811 fi
1812 else
1813 if [[ -f $PWS/$PDIR/$PF ]]; then
1814 # Parent is not a real workspace, but just a raw
1815 # directory tree - use the file that's there as
1816 # the old file.
1818 rm -f $olddir/$DIR/$F
1819 cp $PWS/$PDIR/$PF $olddir/$DIR/$F
1820 fi
1821 fi
1822 }
1824 #
1825 # Find the parent for $1
1826 #
1827 function find_outrev
1828 {
1829 crev=$1
1830 prev=`hg log -r $crev --template '{parents}\n'`
1831 if [[ -z "$prev" ]]
1832 then
1833 # No specific parent means previous changeset is parent
1834 prev=`expr $crev - 1`
1835 else
1836 # Format is either of the following two:
1837 # 546:7df6fcf1183b
1838 # 548:16f1915bb5cd 547:ffaa4e775815
1839 prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
1840 fi
1841 print $prev
1842 }
1844 function extract_ssh_infos
1845 {
1846 CMD=$1
1847 if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
1848 ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
1849 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
1850 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
1851 else
1852 ssh_user=
1853 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
1854 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
1855 fi
1857 }
1859 function build_old_new_mercurial
1860 {
1861 olddir=$1
1862 newdir=$2
1863 DIR=$3
1864 F=$4
1865 #
1866 # new version of the file.
1867 #
1868 rm -rf $newdir/$DIR/$F
1869 if [ -f $F ]; then
1870 cp $F $newdir/$DIR/$F
1871 fi
1873 #
1874 # Old version of the file.
1875 #
1876 rm -rf $olddir/$DIR/$F
1878 if [ -n "$PWS" ]; then
1879 if expr "$PWS" : 'ssh://' >/dev/null
1880 then
1881 extract_ssh_infos $PWS
1882 if [ -n "$ssh_user" ]; then
1883 parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
1884 else
1885 parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
1886 fi
1887 else
1888 parent="hg -R $PWS --cwd $PWS"
1889 fi
1890 else
1891 parent=""
1892 fi
1894 if [ -z "$rename" ]; then
1895 if [ -n "$rflag" ]; then
1896 parentrev=$PARENT_REV
1897 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1898 parentrev=$OUTREV
1899 else
1900 if [[ -n $HG_BRANCH ]]; then
1901 parentrev=$HG_BRANCH
1902 else
1903 parentrev="tip"
1904 fi
1905 fi
1907 if [ -n "$parentrev" ]; then
1908 if [ -z "$parent" ]; then
1909 hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
1910 else
1911 # when specifying a workspace we have to provide
1912 # the full path
1913 $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
1914 fi
1915 fi
1916 else
1917 # It's a rename (or a move), or a copy, so let's make sure we move
1918 # to the right directory first, then restore it once done
1919 current_dir=`pwd`
1920 cd $CWS/$PDIR
1921 if [ -n "$rflag" ]; then
1922 parentrev=$PARENT_REV
1923 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1924 parentrev=$OUTREV
1925 fi
1926 if [ -z "$parentrev" ]; then
1927 parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
1928 fi
1929 if [ -n "$parentrev" ]; then
1930 mkdir -p $olddir/$PDIR
1931 if [ -z "$parent" ]; then
1932 hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
1933 else
1934 $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
1935 fi
1936 fi
1937 cd $current_dir
1938 fi
1939 }
1941 function build_old_new
1942 {
1943 if [[ $SCM_MODE == "teamware" ]]; then
1944 build_old_new_teamware $@
1945 fi
1947 if [[ $SCM_MODE == "mercurial" ]]; then
1948 build_old_new_mercurial $@
1949 fi
1950 }
1953 #
1954 # Usage message.
1955 #
1956 function usage
1957 {
1958 print "Usage:\twebrev [common-options]
1959 webrev [common-options] ( <file> | - )
1960 webrev [common-options] -w <wx file>
1961 webrev [common-options] -l [arguments to 'putback']
1963 Options:
1964 -v: Print the version of this tool.
1965 -b: Do not ignore changes in the amount of white space.
1966 -c <CR#>: Include link to CR (aka bugid) in the main page.
1967 -O: Print bugids/arc cases suitable for OpenJDK.
1968 -i <filename>: Include <filename> in the index.html file.
1969 -o <outdir>: Output webrev to specified directory.
1970 -p <compare-against>: Use specified parent wkspc or basis for comparison
1971 -w <wxfile>: Use specified wx active file.
1972 -u <username>: Use that username instead of 'guessing' one.
1973 -m: Forces the use of Mercurial
1974 -t: Forces the use of Teamware
1976 Mercurial only options:
1977 -r rev: Compare against a specified revision
1978 -N: Skip 'hg outgoing', use only 'hg status'
1979 -f: Use the forest extension
1981 Environment:
1982 WDIR: Control the output directory.
1983 WEBREV_BUGURL: Control the URL prefix for bugids.
1984 WEBREV_SACURL: Control the URL prefix for ARC cases.
1986 SCM Environment:
1987 Teamware: CODEMGR_WS: Workspace location.
1988 Teamware: CODEMGR_PARENT: Parent workspace location.
1990 "
1992 exit 2
1993 }
1995 #
1996 #
1997 # Main program starts here
1998 #
1999 #
2000 LANG="C"
2001 LC_ALL="C"
2002 export LANG LC_ALL
2003 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2005 set +o noclobber
2007 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
2008 [[ -z $WX ]] && WX=`look_for_prog wx`
2009 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
2010 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
2011 [[ -z $PERL ]] && PERL=`look_for_prog perl`
2012 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
2013 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
2014 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
2015 [[ -z $AWK ]] && AWK=`look_for_prog awk`
2016 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
2017 [[ -z $JAR ]] && JAR=`look_for_prog jar`
2018 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
2019 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
2020 [[ -z $WGET ]] && WGET=`look_for_prog wget`
2022 if uname | grep CYGWIN >/dev/null
2023 then
2024 ISWIN=1
2025 # Under windows mercurial outputs '\' instead of '/'
2026 FILTER="tr '\\\\' '/'"
2027 else
2028 FILTER="cat"
2029 fi
2031 if [[ ! -x $PERL ]]; then
2032 print -u2 "Error: No perl interpreter found. Exiting."
2033 exit 1
2034 fi
2036 #
2037 # These aren't fatal, but we want to note them to the user.
2038 # We don't warn on the absence of 'wx' until later when we've
2039 # determined that we actually need to try to invoke it.
2040 #
2041 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
2042 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
2043 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
2045 # Declare global total counters.
2046 integer TOTL TINS TDEL TMOD TUNC
2048 flist_mode=
2049 flist_file=
2050 bflag=
2051 iflag=
2052 oflag=
2053 pflag=
2054 uflag=
2055 lflag=
2056 wflag=
2057 Oflag=
2058 rflag=
2059 Nflag=
2060 forestflag=
2061 while getopts "c:i:o:p:r:u:lmtwONvfb" opt
2062 do
2063 case $opt in
2064 b) bflag=1;;
2066 i) iflag=1
2067 INCLUDE_FILE=$OPTARG;;
2069 o) oflag=1
2070 WDIR=$OPTARG;;
2072 p) pflag=1
2073 codemgr_parent=$OPTARG;;
2075 u) uflag=1
2076 username=$OPTARG;;
2078 c) if [[ -z $CRID ]]; then
2079 CRID=$OPTARG
2080 else
2081 CRID="$CRID $OPTARG"
2082 fi;;
2084 m) SCM_MODE="mercurial";;
2086 t) SCM_MODE="teamware";;
2088 #
2089 # If -l has been specified, we need to abort further options
2090 # processing, because subsequent arguments are going to be
2091 # arguments to 'putback -n'.
2092 #
2093 l) lflag=1
2094 break;;
2096 w) wflag=1;;
2098 O) Oflag=1;;
2100 N) Nflag=1;;
2102 f) forestflag=1;;
2104 r) rflag=1
2105 PARENT_REV=$OPTARG;;
2107 v) print "$0 version: $WEBREV_UPDATED";;
2110 ?) usage;;
2111 esac
2112 done
2114 FLIST=/tmp/$$.flist
2116 if [[ -n $wflag && -n $lflag ]]; then
2117 usage
2118 fi
2120 if [[ -n $forestflag && -n $rflag ]]; then
2121 print "The -r <rev> flag is incompatible with the use of forests"
2122 exit 2
2123 fi
2125 #
2126 # If this manually set as the parent, and it appears to be an earlier webrev,
2127 # then note that fact and set the parent to the raw_files/new subdirectory.
2128 #
2129 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
2130 parent_webrev="$codemgr_parent"
2131 codemgr_parent="$codemgr_parent/raw_files/new"
2132 fi
2134 if [[ -z $wflag && -z $lflag ]]; then
2135 shift $(($OPTIND - 1))
2137 if [[ $1 == "-" ]]; then
2138 cat > $FLIST
2139 flist_mode="stdin"
2140 flist_done=1
2141 shift
2142 elif [[ -n $1 ]]; then
2143 if [[ ! -r $1 ]]; then
2144 print -u2 "$1: no such file or not readable"
2145 usage
2146 fi
2147 cat $1 > $FLIST
2148 flist_mode="file"
2149 flist_file=$1
2150 flist_done=1
2151 shift
2152 else
2153 flist_mode="auto"
2154 fi
2155 fi
2157 #
2158 # Before we go on to further consider -l and -w, work out which SCM we think
2159 # is in use.
2160 #
2161 if [[ -z $SCM_MODE ]]; then
2162 SCM_MODE=`detect_scm $FLIST`
2163 fi
2164 if [[ $SCM_MODE == "unknown" ]]; then
2165 print -u2 "Unable to determine SCM type currently in use."
2166 print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
2167 print -u2 " the environment or in the file list."
2168 print -u2 "For mercurial: webrev runs 'hg root'."
2169 exit 1
2170 fi
2172 print -u2 " SCM detected: $SCM_MODE"
2175 if [[ $SCM_MODE == "mercurial" ]]; then
2176 #
2177 # determine Workspace and parent workspace paths
2178 #
2179 CWS=`hg root | $FILTER`
2180 if [[ -n $pflag && -z "$PWS" ]]; then
2181 OUTPWS=$codemgr_parent
2182 # Let's try to expand it if it's an alias defined in [paths]
2183 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
2184 if [[ -n $tmp ]]; then
2185 OUTPWS="$tmp"
2186 fi
2187 if [[ -n $rflag ]]; then
2188 if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
2189 PWS=$codemgr_parent
2190 else
2191 PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
2192 fi
2193 fi
2194 fi
2195 #
2196 # OUTPWS is the parent repository to use when using 'hg outgoing'
2197 #
2198 if [[ -z $Nflag ]]; then
2199 if [[ -n $forestflag ]]; then
2200 #
2201 # for forest we have to rely on properly set default and
2202 # default-push because they can be different from the top one.
2203 # unless of course it was explicitely speficied with -p
2204 if [[ -z $pflag ]]; then
2205 OUTPWS=
2206 fi
2207 else
2208 #
2209 # Unfortunately mercurial is bugged and doesn't handle
2210 # aliases correctly in 'hg path default'
2211 # So let's do it ourselves. Sigh...
2212 if [[ -z "$OUTPWS" ]]; then
2213 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
2214 fi
2215 # Still empty, means no default-push
2216 if [[ -z "$OUTPWS" ]]; then
2217 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
2218 fi
2219 # Let's try to expand it if it's an alias defined in [paths]
2220 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
2221 if [[ -n $tmp ]]; then
2222 OUTPWS="$tmp"
2223 fi
2224 fi
2225 fi
2226 #
2227 # OUTPWS may contain username:password, let's make sure we remove the
2228 # sensitive information before we print out anything in the HTML
2229 #
2230 OUTPWS2=$OUTPWS
2231 if [[ -n $OUTPWS ]]; then
2232 if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
2233 # Remove everything between '://' and '@'
2234 OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
2235 fi
2236 fi
2238 if [[ -z $HG_BRANCH ]]; then
2239 HG_BRANCH=`hg branch`
2240 if [ "$HG_BRANCH" == "default" ]; then
2241 #
2242 # 'default' means no particular branch, so let's cancel that
2243 #
2244 HG_BRANCH=
2245 fi
2246 fi
2248 if [[ -z $forestflag ]]; then
2249 if [[ -z $Nflag ]]; then
2250 #
2251 # If no "-N", always do "hg outgoing" against parent
2252 # repository to determine list of outgoing revisions.
2253 #
2254 ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
2255 if [[ -n $ALL_CREV ]]; then
2256 FIRST_CREV=`echo "$ALL_CREV" | head -1`
2257 #
2258 # If no "-r", choose revision to compare against by
2259 # finding the latest revision not in the outgoing list.
2260 #
2261 if [[ -z $rflag ]]; then
2262 OUTREV=`find_outrev "$FIRST_CREV"`
2263 if [[ -n $OUTREV ]]; then
2264 HG_LIST_FROM_COMMIT=1
2265 fi
2266 fi
2267 fi
2268 elif [[ -n $rflag ]]; then
2269 #
2270 # If skipping "hg outgoing" but still comparing against a
2271 # specific revision (not the tip), set revision for comment
2272 # accumulation.
2273 #
2274 FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
2275 FIRST_CREV=`expr $FIRST_CREV + 1`
2276 fi
2277 fi
2278 #Let's check if a merge is needed, if so, issue a warning
2279 PREV=`hg parent | grep '^tag:.*tip$'`
2280 if [[ -z $PREV ]]; then
2281 print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
2282 fi
2283 fi
2285 if [[ -n $lflag ]]; then
2286 #
2287 # If the -l flag is given instead of the name of a file list,
2288 # then generate the file list by extracting file names from a
2289 # putback -n.
2290 #
2291 shift $(($OPTIND - 1))
2292 if [[ $SCM_MODE == "teamware" ]]; then
2293 flist_from_teamware "$*"
2294 elif [[ $SCM_MODE == "mercurial" ]]; then
2295 flist_from_mercurial
2296 fi
2297 flist_done=1
2298 shift $#
2300 elif [[ -n $wflag ]]; then
2301 #
2302 # If the -w is given then assume the file list is in Bonwick's "wx"
2303 # command format, i.e. pathname lines alternating with SCCS comment
2304 # lines with blank lines as separators. Use the SCCS comments later
2305 # in building the index.html file.
2306 #
2307 shift $(($OPTIND - 1))
2308 wxfile=$1
2309 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2310 if [[ -r $CODEMGR_WS/wx/active ]]; then
2311 wxfile=$CODEMGR_WS/wx/active
2312 fi
2313 fi
2315 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
2316 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2318 print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2319 flist_from_wx $wxfile
2320 flist_done=1
2321 if [[ -n "$*" ]]; then
2322 shift
2323 fi
2324 elif [[ $flist_mode == "stdin" ]]; then
2325 print -u2 " File list from: standard input"
2326 elif [[ $flist_mode == "file" ]]; then
2327 print -u2 " File list from: $flist_file"
2328 fi
2330 if [[ $# -gt 0 ]]; then
2331 print -u2 "WARNING: unused arguments: $*"
2332 fi
2334 if [[ $SCM_MODE == "teamware" ]]; then
2335 #
2336 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
2337 # be set in a number of ways, in decreasing precedence:
2338 #
2339 # 1) on the command line (only for the parent)
2340 # 2) in the user environment
2341 # 3) in the flist
2342 # 4) automatically based on the workspace (only for the parent)
2343 #
2345 #
2346 # Here is case (2): the user environment
2347 #
2348 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
2349 [[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
2351 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
2352 print -u2 "$codemgr_ws: no such workspace"
2353 exit 1
2354 fi
2356 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
2357 codemgr_parent=$CODEMGR_PARENT
2359 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
2360 print -u2 "$codemgr_parent: no such directory"
2361 exit 1
2362 fi
2364 #
2365 # If we're in auto-detect mode and we haven't already gotten the file
2366 # list, then see if we can get it by probing for wx.
2367 #
2368 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
2369 if [[ ! -x $WX ]]; then
2370 print -u2 "WARNING: wx not found!"
2371 fi
2373 #
2374 # We need to use wx list -w so that we get renamed files, etc.
2375 # but only if a wx active file exists-- otherwise wx will
2376 # hang asking us to initialize our wx information.
2377 #
2378 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
2379 print -u2 " File list from: 'wx list -w' ... \c"
2380 $WX list -w > $FLIST
2381 $WX comments > /tmp/$$.wx_comments
2382 wxfile=/tmp/$$.wx_comments
2383 print -u2 "done"
2384 flist_done=1
2385 fi
2386 fi
2388 #
2389 # If by hook or by crook we've gotten a file list by now (perhaps
2390 # from the command line), eval it to extract environment variables from
2391 # it: This is step (3).
2392 #
2393 env_from_flist
2395 #
2396 # Continuing step (3): If we still have no file list, we'll try to get
2397 # it from teamware.
2398 #
2399 if [[ -z $flist_done ]]; then
2400 flist_from_teamware
2401 env_from_flist
2402 fi
2404 if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
2405 codemgr_ws=$PWD
2406 fi
2407 #
2408 # Observe true directory name of CODEMGR_WS, as used later in
2409 # webrev title.
2410 #
2411 if [[ -n $codemgr_ws ]]; then
2412 codemgr_ws=$(cd $codemgr_ws;print $PWD)
2413 fi
2415 if [[ -n $codemgr_parent ]]; then
2416 codemgr_parent=$(cd $codemgr_parent;print $PWD)
2417 fi
2419 #
2420 # (4) If we still don't have a value for codemgr_parent, get it
2421 # from workspace.
2422 #
2423 [[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
2424 [[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
2426 if [[ ! -d $codemgr_parent ]]; then
2427 print -u2 "$CODEMGR_PARENT: no such parent workspace"
2428 exit 1
2429 fi
2431 #
2432 # Reset CODEMGR_WS to make sure teamware commands are happy.
2433 #
2434 CODEMGR_WS=$codemgr_ws
2435 CWS=$codemgr_ws
2436 PWS=$codemgr_parent
2437 elif [[ $SCM_MODE == "mercurial" ]]; then
2438 if [[ -z $flist_done ]]; then
2439 flist_from_mercurial $PWS
2440 fi
2441 fi
2443 #
2444 # If the user didn't specify a -i option, check to see if there is a
2445 # webrev-info file in the workspace directory.
2446 #
2447 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2448 iflag=1
2449 INCLUDE_FILE="$CWS/webrev-info"
2450 fi
2452 if [[ -n $iflag ]]; then
2453 if [[ ! -r $INCLUDE_FILE ]]; then
2454 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
2455 "not readable."
2456 exit 1
2457 else
2458 #
2459 # $INCLUDE_FILE may be a relative path, and the script alters
2460 # PWD, so we just stash a copy in /tmp.
2461 #
2462 cp $INCLUDE_FILE /tmp/$$.include
2463 fi
2464 fi
2466 #
2467 # Output directory.
2468 #
2469 if [[ -z $WDIR ]]; then
2470 WDIR=$CWS/webrev
2471 else
2472 # If the output directory doesn't end with '/webrev' or '/webrev/'
2473 # then add '/webrev'. This is for backward compatibility
2474 if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
2475 then
2476 WDIR=$WDIR/webrev
2477 fi
2478 fi
2479 # WDIR=${WDIR:-$CWS/webrev}
2481 #
2482 # Name of the webrev, derived from the workspace name; in the
2483 # future this could potentially be an option.
2484 #
2485 # Let's keep what's after the last '/'
2486 WNAME=${CWS##*/}
2488 #
2489 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
2490 #
2491 if [ ${WDIR%%/*} ]; then
2492 if [[ -n $ISWIN ]]; then
2493 if [ ${WDIR%%[A-Za-z]:*} ]; then
2494 WDIR=$PWD/$WDIR
2495 fi
2496 else
2497 WDIR=$PWD/$WDIR
2498 fi
2499 fi
2501 if [[ ! -d $WDIR ]]; then
2502 mkdir -p $WDIR
2503 [[ $? != 0 ]] && exit 1
2504 fi
2506 #
2507 # Summarize what we're going to do.
2508 #
2509 print " Workspace: $CWS"
2510 if [[ -n $parent_webrev ]]; then
2511 print "Compare against: webrev at $parent_webrev"
2512 elif [[ -n $OUTPWS2 ]]; then
2513 print "Compare against: $OUTPWS2"
2514 fi
2515 if [[ -n $HG_BRANCH ]]; then
2516 print " Branch: $HG_BRANCH"
2517 fi
2518 if [[ -n $rflag ]]; then
2519 print "Compare against version: $PARENT_REV"
2520 fi
2521 [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE"
2522 print " Output to: $WDIR"
2524 #
2525 # Save the file list in the webrev dir
2526 #
2527 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
2529 #
2530 # Bug IDs will be replaced by a URL. Order of precedence
2531 # is: default location, $WEBREV_BUGURL, the -O flag.
2532 #
2533 BUGURL='https://jbs.oracle.com/bugs/browse/'
2534 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
2535 if [[ -n "$Oflag" ]]; then
2536 CRID=`echo $CRID | sed -e 's/JDK-//'`
2537 BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
2538 IDPREFIX=''
2539 else
2540 IDPREFIX='JDK-'
2541 fi
2544 #
2545 # Likewise, ARC cases will be replaced by a URL. Order of precedence
2546 # is: default, $WEBREV_SACURL, the -O flag.
2547 #
2548 # Note that -O also triggers different substitution behavior for
2549 # SACURL. See sac2url().
2550 #
2551 SACURL='http://sac.eng.sun.com'
2552 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
2553 [[ -n $Oflag ]] && \
2554 SACURL='http://www.opensolaris.org/os/community/arc/caselog'
2556 rm -f $WDIR/$WNAME.patch
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 [[ -v NO_OUTGOING ]];
2787 then
2788 # Only need to generate a patch file here if there are no commits in outgoing
2789 rm -f $WDIR/$DIR/$F.patch
2790 if [[ -z $rename ]]; then
2791 if [ ! -f $ofile ]; then
2792 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2793 > $WDIR/$DIR/$F.patch
2794 elif [ ! -f $nfile ]; then
2795 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2796 > $WDIR/$DIR/$F.patch
2797 else
2798 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
2799 fi
2800 else
2801 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2802 > $WDIR/$DIR/$F.patch
2804 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2805 >> $WDIR/$DIR/$F.patch
2807 fi
2810 #
2811 # Tack the patch we just made onto the accumulated patch for the
2812 # whole wad.
2813 #
2814 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
2815 fi
2817 print " patch\c"
2819 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2821 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
2822 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2823 > $WDIR/$DIR/$F.cdiff.html
2824 print " cdiffs\c"
2826 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
2827 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2828 > $WDIR/$DIR/$F.udiff.html
2830 print " udiffs\c"
2832 if [[ -x $WDIFF ]]; then
2833 $WDIFF -c "$COMM" \
2834 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
2835 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2836 if [[ $? -eq 0 ]]; then
2837 print " wdiffs\c"
2838 else
2839 print " wdiffs[fail]\c"
2840 fi
2841 fi
2843 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2844 > $WDIR/$DIR/$F.sdiff.html
2845 print " sdiffs\c"
2847 print " frames\c"
2849 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2851 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2853 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2854 # renamed file: may also have differences
2855 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2856 elif [[ -f $nfile ]]; then
2857 # new file: count added lines
2858 difflines /dev/null $nfile > $WDIR/$DIR/$F.count
2859 elif [[ -f $ofile ]]; then
2860 # old file: count deleted lines
2861 difflines $ofile /dev/null > $WDIR/$DIR/$F.count
2862 fi
2863 fi
2864 #
2865 # Now we generate the postscript for this file. We generate diffs
2866 # only in the event that there is delta, or the file is new (it seems
2867 # tree-killing to print out the contents of deleted files).
2868 #
2869 if [[ -f $nfile ]]; then
2870 ocr=$ofile
2871 [[ ! -f $ofile ]] && ocr=/dev/null
2873 if [[ -z $mv_but_nodiff ]]; then
2874 textcomm=`getcomments text $P $PP`
2875 if [[ -x $CODEREVIEW ]]; then
2876 $CODEREVIEW -y "$textcomm" \
2877 -e $ocr $nfile \
2878 > /tmp/$$.psfile 2>/dev/null &&
2879 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
2880 if [[ $? -eq 0 ]]; then
2881 print " ps\c"
2882 else
2883 print " ps[fail]\c"
2884 fi
2885 fi
2886 fi
2887 fi
2889 if [[ -f $ofile && -z $mv_but_nodiff ]]; then
2890 if [[ -n $its_a_jar ]]; then
2891 source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
2892 else
2893 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
2894 fi
2895 print " old\c"
2896 fi
2898 if [[ -f $nfile ]]; then
2899 if [[ -n $its_a_jar ]]; then
2900 source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
2901 else
2902 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
2903 fi
2904 print " new\c"
2905 fi
2907 print
2908 done < $FLIST
2910 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
2911 if [[ $SCM_MODE == "mercurial" ]]; then
2912 if [[ !(-v NO_OUTGOING) ]]; then
2913 EXPORTCHANGESET="$WNAME.changeset"
2914 CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
2915 rm -f $CHANGESETPATH
2916 touch $CHANGESETPATH
2917 if [[ -n $ALL_CREV ]]; then
2918 rev_opt=
2919 for rev in $ALL_CREV; do
2920 rev_opt="$rev_opt --rev $rev"
2921 done
2922 elif [[ -n $FIRST_CREV ]]; then
2923 rev_opt="--rev $FIRST_CREV"
2924 fi
2926 if [[ -n $rev_opt ]]; then
2927 (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
2928 # echo "Created new-patch: $CHANGESETPATH" 1>&2
2929 # Use it in place of the jdk.patch created above
2930 rm -f $WDIR/$WNAME.patch
2931 fi
2932 set +x
2933 fi
2934 fi
2936 frame_nav_js > $WDIR/ancnav.js
2937 frame_navigation > $WDIR/ancnav.html
2939 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
2940 print " Generating PDF: \c"
2941 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
2942 print "Done."
2943 fi
2945 # Now build the index.html file that contains
2946 # links to the source files and their diffs.
2948 cd $CWS
2950 # Save total changed lines for Code Inspection.
2951 print "$TOTL" > $WDIR/TotalChangedLines
2953 print " index.html: \c"
2954 INDEXFILE=$WDIR/index.html
2955 exec 3<&1 # duplicate stdout to FD3.
2956 exec 1<&- # Close stdout.
2957 exec > $INDEXFILE # Open stdout to index file.
2959 print "$HTML<head>"
2960 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
2961 print "$STDHEAD"
2962 print "<title>$WNAME</title>"
2963 print "</head>"
2964 print "<body id=\"SUNWwebrev\">"
2965 print "<div class=\"summary\">"
2966 print "<h2>Code Review for $WNAME</h2>"
2968 print "<table>"
2970 if [[ -z $uflag ]]
2971 then
2972 if [[ $SCM_MODE == "mercurial" ]]
2973 then
2974 #
2975 # Let's try to extract the user name from the .hgrc file
2976 #
2977 username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
2978 fi
2980 if [[ -z $username ]]
2981 then
2982 #
2983 # Figure out the username and gcos name. To maintain compatibility
2984 # with passwd(4), we must support '&' substitutions.
2985 #
2986 username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
2987 if [[ -x $GETENT ]]; then
2988 realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
2989 fi
2990 userupper=`print "$username" | sed 's/\<./\u&/g'`
2991 realname=`print $realname | sed s/\&/$userupper/`
2992 fi
2993 fi
2995 date="on `date`"
2997 if [[ -n "$username" && -n "$realname" ]]; then
2998 print "<tr><th>Prepared by:</th>"
2999 print "<td>$realname ($username) $date</td></tr>"
3000 elif [[ -n "$username" ]]; then
3001 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
3002 fi
3004 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
3005 if [[ -n $parent_webrev ]]; then
3006 print "<tr><th>Compare against:</th><td>"
3007 print "webrev at $parent_webrev"
3008 else
3009 if [[ -n $OUTPWS2 ]]; then
3010 print "<tr><th>Compare against:</th><td>"
3011 print "$OUTPWS2"
3012 fi
3013 fi
3014 print "</td></tr>"
3015 if [[ -n $rflag ]]; then
3016 print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
3017 elif [[ -n $OUTREV ]]; then
3018 if [[ -z $forestflag ]]; then
3019 print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
3020 fi
3021 fi
3022 if [[ -n $HG_BRANCH ]]; then
3023 print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
3024 fi
3026 print "<tr><th>Summary of changes:</th><td>"
3027 printCI $TOTL $TINS $TDEL $TMOD $TUNC
3028 print "</td></tr>"
3030 if [[ -f $WDIR/$WNAME.patch ]]; then
3031 print "<tr><th>Patch of changes:</th><td>"
3032 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
3033 elif [[ -f $CHANGESETPATH ]]; then
3034 print "<tr><th>Changeset:</th><td>"
3035 print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
3036 fi
3038 if [[ -f $WDIR/$WNAME.pdf ]]; then
3039 print "<tr><th>Printable review:</th><td>"
3040 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
3041 fi
3043 if [[ -n "$iflag" ]]; then
3044 print "<tr><th>Author comments:</th><td><div>"
3045 cat /tmp/$$.include
3046 print "</div></td></tr>"
3047 fi
3048 # Add links to referenced CRs, if any
3049 # external URL has a <title> like:
3050 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
3051 # while internal URL has <title> like:
3052 # <title>[#JDK-6641309] Wrong Cookie separator used in HttpURLConnection</title>
3053 #
3054 if [[ -n $CRID ]]; then
3055 for id in $CRID
3056 do
3057 if [[ -z "$Oflag" ]]; then
3058 #add "JDK-" to raw bug id for jbs links.
3059 id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
3060 fi
3061 print "<tr><th>Bug id:</th><td>"
3062 url="${BUGURL}${id}"
3063 if [[ -n "$Oflag" ]]; then
3064 cleanup='s/Bug ID: \([0-9]\{5,\}\) \(.*\)/JDK-\1 : \2/'
3065 else
3066 cleanup='s|\[#\(JDK-[0-9]\{5,\}\)\] \(.*\)|\1 : \2|'
3067 fi
3068 if [[ -n $WGET ]]; then
3069 msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed "$cleanup" | html_quote`
3070 fi
3071 if [[ -z $msg ]]; then
3072 msg="${id}"
3073 fi
3075 print "<a href=\"$url\">$msg</a>"
3077 print "</td></tr>"
3078 done
3079 fi
3080 print "<tr><th>Legend:</th><td>"
3081 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>"
3082 print "</table>"
3083 print "</div>"
3085 #
3086 # Second pass through the files: generate the rest of the index file
3087 #
3088 while read LINE
3089 do
3090 set - $LINE
3091 if [[ $1 == "Revision:" ]]; then
3092 FIRST_CREV=`expr $3 + 1`
3093 continue
3094 fi
3095 P=$1
3097 if [[ $# == 2 ]]; then
3098 PP=$2
3099 oldname=" <i>(was $PP)</i>"
3101 else
3102 PP=$P
3103 oldname=""
3104 fi
3106 DIR=${P%/*}
3107 if [[ $DIR == $P ]]; then
3108 DIR="." # File at root of workspace
3109 fi
3111 # Avoid processing the same file twice.
3112 # It's possible for renamed files to
3113 # appear twice in the file list
3115 F=$WDIR/$P
3117 print "<p><code>"
3119 # If there's a diffs file, make diffs links
3121 NODIFFS=
3122 if [[ -f $F.cdiff.html ]]; then
3123 print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
3124 print "<a href=\"$P.udiff.html\">Udiffs</a>"
3126 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
3127 print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
3128 fi
3130 print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
3132 print "<a href=\"$P.frames.html\">Frames</a>"
3133 else
3134 NODIFFS=1
3135 print " ------ ------ ------"
3137 if [[ -x $WDIFF ]]; then
3138 print " ------"
3139 fi
3141 print " ------"
3142 fi
3144 # If there's an old file, make the link
3146 NOOLD=
3147 if [[ -f $F-.html ]]; then
3148 print "<a href=\"$P-.html\">Old</a>"
3149 else
3150 NOOLD=1
3151 print " ---"
3152 fi
3154 # If there's an new file, make the link
3156 NONEW=
3157 if [[ -f $F.html ]]; then
3158 print "<a href=\"$P.html\">New</a>"
3159 else
3160 NONEW=1
3161 print " ---"
3162 fi
3164 if [[ -f $F.patch ]]; then
3165 print "<a href=\"$P.patch\">Patch</a>"
3166 else
3167 print " -----"
3168 fi
3170 if [[ -f $WDIR/raw_files/new/$P ]]; then
3171 print "<a href=\"raw_files/new/$P\">Raw</a>"
3172 else
3173 print " ---"
3174 fi
3175 print "</code>"
3176 if [[ -n $NODIFFS && -z $oldname ]]; then
3177 if [[ -n $NOOLD ]]; then
3178 print "<font color=green><b>$P</b></font>"
3179 elif [[ -n $NONEW ]]; then
3180 print "<font color=red><b>$P</b></font>"
3181 fi
3182 else
3183 print "<b>$P</b> $oldname"
3184 fi
3186 #
3187 # Check for usr/closed
3188 #
3189 if [ ! -z "$Oflag" ]; then
3190 if [[ $P == usr/closed/* ]]; then
3191 print " <i>Closed source: omitted from" \
3192 "this review</i>"
3193 fi
3194 fi
3196 print "</p><blockquote>\c"
3197 # Insert delta comments if any
3198 comments=`getcomments html $P $PP`
3199 if [ -n "$comments" ]; then
3200 print "<pre>$comments</pre>"
3201 fi
3203 # Add additional comments comment
3205 print "<!-- Add comments to explain changes in $P here -->"
3207 # Add count of changes.
3209 if [[ -f $F.count ]]; then
3210 cat $F.count
3211 rm $F.count
3212 fi
3213 print "</blockquote>"
3214 done < $FLIST
3216 print
3217 print
3218 print "<hr />"
3219 print "<p style=\"font-size: small\">"
3220 print "This code review page was prepared using <b>$0</b>"
3221 print "(vers $WEBREV_UPDATED)."
3222 print "</body>"
3223 print "</html>"
3225 if [[ -n $ZIP ]]; then
3226 # Let's generate a zip file for convenience
3227 cd $WDIR/..
3228 if [ -f webrev.zip ]; then
3229 rm webrev.zip
3230 fi
3231 $ZIP -r webrev webrev >/dev/null 2>&1
3232 fi
3234 exec 1<&- # Close FD 1.
3235 exec 1<&3 # dup FD 3 to restore stdout.
3236 exec 3<&- # close FD 3.
3238 print "Done."
3239 print "Output to: $WDIR"