Tue, 13 Sep 2011 08:37:34 -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, 2011, 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
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'&\">&</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="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
234 # 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=0
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
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
1479 $HGCMD -aC | $FILTER | while read LINE; do
1480 ldone=""
1481 while [ -z "$ldone" ]; do
1482 ldone="1"
1483 set - $LINE
1484 if [ $# -eq 2 -a "$1" == "A" ]; then
1485 AFILE=$2
1486 if read LINE2; then
1487 set - $LINE2
1488 if [ $# -eq 1 ]; then
1489 echo $TREE/$AFILE $TREE/$1 >>$FLIST
1490 elif [ $# -eq 2 ]; then
1491 echo $TREE/$AFILE >>$FLIST
1492 LINE=$LINE2
1493 ldone=""
1494 fi
1495 else
1496 echo $TREE/$AFILE >>$FLIST
1497 fi
1498 fi
1499 done
1500 done
1501 $HGCMD -rn | $FILTER | while read RFILE; do
1502 grep "$TREE/$RFILE" $FLIST >/dev/null
1503 if [ $? -eq 1 ]; then
1504 echo $TREE/$RFILE >>$FLIST
1505 fi
1506 done
1507 }
1509 function fstatus
1510 {
1511 #
1512 # forest extension is still being changed. For instance the output
1513 # of fstatus used to no prepend the tree path to filenames, but
1514 # this has changed recently. AWK code below does try to handle both
1515 # cases
1516 #
1517 hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1518 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1519 $1 != "" {n=index($1,tree);
1520 if (n == 0)
1521 { printf("%s/%s\n",tree,$1)}
1522 else
1523 { printf("%s\n",$1)}}' >> $FLIST
1525 #
1526 # There is a bug in the output of fstatus -aC on recent versions: it
1527 # inserts a space between the name of the tree and the filename of the
1528 # old file. e.g.:
1529 #
1530 # $ hg fstatus -aC
1531 # [.]
1532 #
1533 # [MyWS]
1534 # A MyWS/subdir/File2
1535 # MyWS/ File2
1536 #
1537 # [MyWS2]
1538 #
1540 hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1541 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1542 /^A .*/ {n=index($2,tree);
1543 if (n == 0)
1544 { printf("A %s/%s\n",tree,$2)}
1545 else
1546 { printf("A %s\n",$2)};
1547 next}
1548 /^ / {n=index($1,tree);
1549 if (n == 0)
1550 { printf("%s/%s\n",tree,$1)}
1551 else
1552 { if (NF == 2)
1553 printf("%s/%s\n",tree,$2)
1554 else
1555 printf("%s\n",$1)
1556 };
1557 next}
1558 ' | while read LINE; do
1559 ldone=""
1560 while [ -z "$ldone" ]; do
1561 ldone="1"
1562 set - $LINE
1563 if [ $# -eq 2 -a "$1" == "A" ]; then
1564 AFILE=$2
1565 if read LINE2; then
1566 set - $LINE2
1567 if [ $# -eq 1 ]; then
1568 echo $AFILE $1 >>$FLIST
1569 elif [ $# -eq 2 ]; then
1570 echo $AFILE >>$FLIST
1571 LINE=$LINE2
1572 ldone=""
1573 fi
1574 else
1575 echo $AFILE >>$FLIST
1576 fi
1577 fi
1578 done
1579 done
1580 hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
1581 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next}
1582 $1 != "" {n=index($1,tree);
1583 if (n == 0)
1584 { printf("%s/%s\n",tree,$1)}
1585 else
1586 { printf("%s\n",$1)}}' | while read RFILE; do
1587 grep "$RFILE" $FLIST >/dev/null
1588 if [ $? -eq 1 ]; then
1589 echo $RFILE >>$FLIST
1590 fi
1591 done
1592 }
1594 #
1595 # flist_from_mercurial $PWS
1596 #
1597 # Only local file based repositories are supported at present
1598 # since even though we can determine the list from the parent finding
1599 # the changes is harder.
1600 #
1601 # We first look for any outgoing files, this is for when the user has
1602 # run hg commit. If we don't find any then we look with hg status.
1603 #
1604 # We need at least one of default-push or default paths set in .hg/hgrc
1605 # If neither are set we don't know who to compare with.
1607 function flist_from_mercurial
1608 {
1609 # if [ "${PWS##ssh://}" != "$PWS" -o \
1610 # "${PWS##http://}" != "$PWS" -o \
1611 # "${PWS##https://}" != "$PWS" ]; then
1612 # print "Remote Mercurial repositories not currently supported."
1613 # print "Set default and/or default-push to a local repository"
1614 # exit
1615 # fi
1616 if [[ -n $forestflag ]]; then
1617 HG_LIST_FROM_COMMIT=
1618 flist_from_mercurial_forest
1619 else
1620 STATUS_REV=
1621 if [[ -n $rflag ]]; then
1622 STATUS_REV="--rev $PARENT_REV"
1623 elif [[ -n $OUTREV ]]; then
1624 STATUS_REV="--rev $OUTREV"
1625 else
1626 # hg commit hasn't been run see what is lying around
1627 print "\n No outgoing, perhaps you haven't commited."
1628 fi
1629 # First let's list all the modified or deleted files
1631 hg status $STATUS_REV -mdn | $FILTER > $FLIST
1633 # Then all the added files
1634 # But some of these could have been "moved" or renamed ones
1635 # so let's make sure we get the proper info
1636 # hg status -aC will produce something like:
1637 # A subdir/File3
1638 # A subdir/File4
1639 # File4
1640 # A subdir/File5
1641 # The first and last are simple addition while the middle one
1642 # is a move/rename
1644 hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
1645 while read LINE; do
1646 ldone=""
1647 while [ -z "$ldone" ]; do
1648 ldone="1"
1649 set - $LINE
1650 if [ $# -eq 2 -a "$1" == "A" ]; then
1651 AFILE=$2
1652 if read LINE2; then
1653 set - $LINE2
1654 if [ $# -eq 1 ]; then
1655 echo $AFILE $1 >>$FLIST
1656 elif [ $# -eq 2 ]; then
1657 echo $AFILE >>$FLIST
1658 LINE=$LINE2
1659 ldone=""
1660 fi
1661 else
1662 echo $AFILE >>$FLIST
1663 fi
1664 fi
1665 done
1666 done < $FLIST.temp
1667 hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
1668 while read RFILE; do
1669 grep "$RFILE" $FLIST >/dev/null
1670 if [ $? -eq 1 ]; then
1671 echo $RFILE >>$FLIST
1672 fi
1673 done < $FLIST.temp
1674 rm -f $FLIST.temp
1675 fi
1676 }
1678 function env_from_flist
1679 {
1680 [[ -r $FLIST ]] || return
1682 #
1683 # Use "eval" to set env variables that are listed in the file
1684 # list. Then copy those into our local versions of those
1685 # variables if they have not been set already.
1686 #
1687 eval `sed -e "s/#.*$//" $FLIST | grep = `
1689 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
1691 #
1692 # Check to see if CODEMGR_PARENT is set in the flist file.
1693 #
1694 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
1695 codemgr_parent=$CODEMGR_PARENT
1696 }
1698 #
1699 # detect_scm
1700 #
1701 # We dynamically test the SCM type; this allows future extensions to
1702 # new SCM types
1703 #
1704 function detect_scm
1705 {
1706 #
1707 # If CODEMGR_WS is specified in the flist file, we assume teamware.
1708 #
1709 if [[ -r $FLIST ]]; then
1710 egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
1711 if [[ $? -eq 0 ]]; then
1712 print "teamware"
1713 return
1714 fi
1715 fi
1717 #
1718 # The presence of $CODEMGR_WS and a Codemgr_wsdata directory
1719 # is our clue that this is a teamware workspace.
1720 # Same if true if current directory has a Codemgr_wsdata sub-dir
1721 #
1722 if [[ -z "$CODEMGR_WS" ]]; then
1723 CODEMGR_WS=`workspace name 2>/dev/null`
1724 fi
1726 if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
1727 print "teamware"
1728 elif [[ -d $PWD/Codemgr_wsdata ]]; then
1729 print "teamware"
1730 elif hg root >/dev/null ; then
1731 print "mercurial"
1732 else
1733 print "unknown"
1734 fi
1735 }
1737 #
1738 # Extract the parent workspace from the Codemgr_wsdata/parent file
1739 #
1740 function parent_from_teamware
1741 {
1742 if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
1743 tail -1 "$1/Codemgr_wsdata/parent"
1744 fi
1745 }
1747 function look_for_prog
1748 {
1749 typeset path
1750 typeset ppath
1751 typeset progname=$1
1753 DEVTOOLS=
1754 OS=`uname`
1755 if [[ "$OS" == "SunOS" ]]; then
1756 DEVTOOLS="/java/devtools/`uname -p`/bin"
1757 elif [[ "$OS" == "Linux" ]]; then
1758 DEVTOOLS="/java/devtools/linux/bin"
1759 fi
1761 ppath=$PATH
1762 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1763 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
1764 ppath=$ppath:/opt/onbld/bin/`uname -p`
1765 ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
1767 PATH=$ppath prog=`whence $progname`
1768 if [[ -n $prog ]]; then
1769 print $prog
1770 fi
1771 }
1773 function build_old_new_teamware
1774 {
1775 # If the child's version doesn't exist then
1776 # get a readonly copy.
1778 if [[ ! -f $F && -f SCCS/s.$F ]]; then
1779 $SCCS get -s $F
1780 fi
1782 #
1783 # Snag new version of file.
1784 #
1785 rm -f $newdir/$DIR/$F
1786 cp $F $newdir/$DIR/$F
1788 #
1789 # Get the parent's version of the file. First see whether the
1790 # child's version is checked out and get the parent's version
1791 # with keywords expanded or unexpanded as appropriate.
1792 #
1793 if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
1794 -f $PWS/$PDIR/SCCS/p.$PF ]; then
1795 rm -f $olddir/$PDIR/$PF
1796 if [ -f SCCS/p.$F ]; then
1797 $SCCS get -s -p -k $PWS/$PDIR/$PF \
1798 > $olddir/$PDIR/$PF
1799 else
1800 $SCCS get -s -p $PWS/$PDIR/$PF \
1801 > $olddir/$PDIR/$PF
1802 fi
1803 else
1804 if [[ -f $PWS/$PDIR/$PF ]]; then
1805 # Parent is not a real workspace, but just a raw
1806 # directory tree - use the file that's there as
1807 # the old file.
1809 rm -f $olddir/$DIR/$F
1810 cp $PWS/$PDIR/$PF $olddir/$DIR/$F
1811 fi
1812 fi
1813 }
1815 #
1816 # Find the parent for $1
1817 #
1818 function find_outrev
1819 {
1820 crev=$1
1821 prev=`hg log -r $crev --template '{parents}\n'`
1822 if [[ -z "$prev" ]]
1823 then
1824 # No specific parent means previous changeset is parent
1825 prev=`expr $crev - 1`
1826 else
1827 # Format is either of the following two:
1828 # 546:7df6fcf1183b
1829 # 548:16f1915bb5cd 547:ffaa4e775815
1830 prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
1831 fi
1832 print $prev
1833 }
1835 function extract_ssh_infos
1836 {
1837 CMD=$1
1838 if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
1839 ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
1840 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
1841 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
1842 else
1843 ssh_user=
1844 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
1845 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
1846 fi
1848 }
1850 function build_old_new_mercurial
1851 {
1852 olddir=$1
1853 newdir=$2
1854 DIR=$3
1855 F=$4
1856 #
1857 # new version of the file.
1858 #
1859 rm -rf $newdir/$DIR/$F
1860 if [ -f $F ]; then
1861 cp $F $newdir/$DIR/$F
1862 fi
1864 #
1865 # Old version of the file.
1866 #
1867 rm -rf $olddir/$DIR/$F
1869 if [ -n "$PWS" ]; then
1870 if expr "$PWS" : 'ssh://' >/dev/null
1871 then
1872 extract_ssh_infos $PWS
1873 if [ -n "$ssh_user" ]; then
1874 parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
1875 else
1876 parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
1877 fi
1878 else
1879 parent="hg -R $PWS --cwd $PWS"
1880 fi
1881 else
1882 parent=""
1883 fi
1885 if [ -z "$rename" ]; then
1886 if [ -n "$rflag" ]; then
1887 parentrev=$PARENT_REV
1888 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1889 parentrev=$OUTREV
1890 else
1891 if [[ -n $HG_BRANCH ]]; then
1892 parentrev=$HG_BRANCH
1893 else
1894 parentrev="tip"
1895 fi
1896 fi
1898 if [ -n "$parentrev" ]; then
1899 if [ -z "$parent" ]; then
1900 hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
1901 else
1902 # when specifying a workspace we have to provide
1903 # the full path
1904 $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
1905 fi
1906 fi
1907 else
1908 # It's a rename (or a move), so let's make sure we move
1909 # to the right directory first, then restore it once done
1910 current_dir=`pwd`
1911 cd $CWS/$PDIR
1912 if [ -n "$rflag" ]; then
1913 parentrev=$PARENT_REV
1914 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
1915 parentrev=$OUTREV
1916 fi
1917 if [ -z "$parentrev" ]; then
1918 parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
1919 fi
1920 if [ -n "$parentrev" ]; then
1921 mkdir -p $olddir/$PDIR
1922 if [ -z "$parent" ]; then
1923 hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
1924 else
1925 $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
1926 fi
1927 fi
1928 cd $current_dir
1929 fi
1930 }
1932 function build_old_new
1933 {
1934 if [[ $SCM_MODE == "teamware" ]]; then
1935 build_old_new_teamware $@
1936 fi
1938 if [[ $SCM_MODE == "mercurial" ]]; then
1939 build_old_new_mercurial $@
1940 fi
1941 }
1944 #
1945 # Usage message.
1946 #
1947 function usage
1948 {
1949 print "Usage:\twebrev [common-options]
1950 webrev [common-options] ( <file> | - )
1951 webrev [common-options] -w <wx file>
1952 webrev [common-options] -l [arguments to 'putback']
1954 Options:
1955 -v: Print the version of this tool.
1956 -b: Do not ignore changes in the amount of white space.
1957 -c <CR#>: Include link to CR (aka bugid) in the main page.
1958 -O: Print bugids/arc cases suitable for OpenJDK.
1959 -i <filename>: Include <filename> in the index.html file.
1960 -o <outdir>: Output webrev to specified directory.
1961 -p <compare-against>: Use specified parent wkspc or basis for comparison
1962 -w <wxfile>: Use specified wx active file.
1963 -u <username>: Use that username instead of 'guessing' one.
1964 -m: Forces the use of Mercurial
1965 -t: Forces the use of Teamware
1967 Mercurial only options:
1968 -r rev: Compare against a specified revision
1969 -N: Skip 'hg outgoing', use only 'hg status'
1970 -f: Use the forest extension
1972 Environment:
1973 WDIR: Control the output directory.
1974 WEBREV_BUGURL: Control the URL prefix for bugids.
1975 WEBREV_SACURL: Control the URL prefix for ARC cases.
1977 SCM Environment:
1978 Teamware: CODEMGR_WS: Workspace location.
1979 Teamware: CODEMGR_PARENT: Parent workspace location.
1981 "
1983 exit 2
1984 }
1986 #
1987 #
1988 # Main program starts here
1989 #
1990 #
1991 LANG="C"
1992 LC_ALL="C"
1993 export LANG LC_ALL
1994 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
1996 set +o noclobber
1998 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
1999 [[ -z $WX ]] && WX=`look_for_prog wx`
2000 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
2001 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
2002 [[ -z $PERL ]] && PERL=`look_for_prog perl`
2003 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
2004 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
2005 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
2006 [[ -z $AWK ]] && AWK=`look_for_prog awk`
2007 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
2008 [[ -z $JAR ]] && JAR=`look_for_prog jar`
2009 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
2010 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
2011 [[ -z $WGET ]] && WGET=`look_for_prog wget`
2013 if uname | grep CYGWIN >/dev/null
2014 then
2015 ISWIN=1
2016 # Under windows mercurial outputs '\' instead of '/'
2017 FILTER="tr '\\\\' '/'"
2018 else
2019 FILTER="cat"
2020 fi
2022 if [[ ! -x $PERL ]]; then
2023 print -u2 "Error: No perl interpreter found. Exiting."
2024 exit 1
2025 fi
2027 #
2028 # These aren't fatal, but we want to note them to the user.
2029 # We don't warn on the absence of 'wx' until later when we've
2030 # determined that we actually need to try to invoke it.
2031 #
2032 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
2033 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
2034 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
2036 # Declare global total counters.
2037 integer TOTL TINS TDEL TMOD TUNC
2039 flist_mode=
2040 flist_file=
2041 bflag=
2042 iflag=
2043 oflag=
2044 pflag=
2045 uflag=
2046 lflag=
2047 wflag=
2048 Oflag=
2049 rflag=
2050 Nflag=
2051 forestflag=
2052 while getopts "c:i:o:p:r:u:lmtwONvfb" opt
2053 do
2054 case $opt in
2055 b) bflag=1;;
2057 i) iflag=1
2058 INCLUDE_FILE=$OPTARG;;
2060 o) oflag=1
2061 WDIR=$OPTARG;;
2063 p) pflag=1
2064 codemgr_parent=$OPTARG;;
2066 u) uflag=1
2067 username=$OPTARG;;
2069 c) if [[ -z $CRID ]]; then
2070 CRID=$OPTARG
2071 else
2072 CRID="$CRID $OPTARG"
2073 fi;;
2075 m) SCM_MODE="mercurial";;
2077 t) SCM_MODE="teamware";;
2079 #
2080 # If -l has been specified, we need to abort further options
2081 # processing, because subsequent arguments are going to be
2082 # arguments to 'putback -n'.
2083 #
2084 l) lflag=1
2085 break;;
2087 w) wflag=1;;
2089 O) Oflag=1;;
2091 N) Nflag=1;;
2093 f) forestflag=1;;
2095 r) rflag=1
2096 PARENT_REV=$OPTARG;;
2098 v) print "$0 version: $WEBREV_UPDATED";;
2101 ?) usage;;
2102 esac
2103 done
2105 FLIST=/tmp/$$.flist
2107 if [[ -n $wflag && -n $lflag ]]; then
2108 usage
2109 fi
2111 if [[ -n $forestflag && -n $rflag ]]; then
2112 print "The -r <rev> flag is incompatible with the use of forests"
2113 exit 2
2114 fi
2116 #
2117 # If this manually set as the parent, and it appears to be an earlier webrev,
2118 # then note that fact and set the parent to the raw_files/new subdirectory.
2119 #
2120 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
2121 parent_webrev="$codemgr_parent"
2122 codemgr_parent="$codemgr_parent/raw_files/new"
2123 fi
2125 if [[ -z $wflag && -z $lflag ]]; then
2126 shift $(($OPTIND - 1))
2128 if [[ $1 == "-" ]]; then
2129 cat > $FLIST
2130 flist_mode="stdin"
2131 flist_done=1
2132 shift
2133 elif [[ -n $1 ]]; then
2134 if [[ ! -r $1 ]]; then
2135 print -u2 "$1: no such file or not readable"
2136 usage
2137 fi
2138 cat $1 > $FLIST
2139 flist_mode="file"
2140 flist_file=$1
2141 flist_done=1
2142 shift
2143 else
2144 flist_mode="auto"
2145 fi
2146 fi
2148 #
2149 # Before we go on to further consider -l and -w, work out which SCM we think
2150 # is in use.
2151 #
2152 if [[ -z $SCM_MODE ]]; then
2153 SCM_MODE=`detect_scm $FLIST`
2154 fi
2155 if [[ $SCM_MODE == "unknown" ]]; then
2156 print -u2 "Unable to determine SCM type currently in use."
2157 print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
2158 print -u2 " the environment or in the file list."
2159 print -u2 "For mercurial: webrev runs 'hg root'."
2160 exit 1
2161 fi
2163 print -u2 " SCM detected: $SCM_MODE"
2166 if [[ $SCM_MODE == "mercurial" ]]; then
2167 #
2168 # determine Workspace and parent workspace paths
2169 #
2170 CWS=`hg root | $FILTER`
2171 if [[ -n $pflag && -z "$PWS" ]]; then
2172 OUTPWS=$codemgr_parent
2173 # Let's try to expand it if it's an alias defined in [paths]
2174 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
2175 if [[ -n $tmp ]]; then
2176 OUTPWS="$tmp"
2177 fi
2178 if [[ -n $rflag ]]; then
2179 if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
2180 PWS=$codemgr_parent
2181 else
2182 PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
2183 fi
2184 fi
2185 fi
2186 #
2187 # OUTPWS is the parent repository to use when using 'hg outgoing'
2188 #
2189 if [[ -z $Nflag ]]; then
2190 if [[ -n $forestflag ]]; then
2191 #
2192 # for forest we have to rely on properly set default and
2193 # default-push because they can be different from the top one.
2194 # unless of course it was explicitely speficied with -p
2195 if [[ -z $pflag ]]; then
2196 OUTPWS=
2197 fi
2198 else
2199 #
2200 # Unfortunately mercurial is bugged and doesn't handle
2201 # aliases correctly in 'hg path default'
2202 # So let's do it ourselves. Sigh...
2203 if [[ -z "$OUTPWS" ]]; then
2204 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
2205 fi
2206 # Still empty, means no default-push
2207 if [[ -z "$OUTPWS" ]]; then
2208 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
2209 fi
2210 # Let's try to expand it if it's an alias defined in [paths]
2211 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
2212 if [[ -n $tmp ]]; then
2213 OUTPWS="$tmp"
2214 fi
2215 fi
2216 fi
2217 #
2218 # OUTPWS may contain username:password, let's make sure we remove the
2219 # sensitive information before we print out anything in the HTML
2220 #
2221 OUTPWS2=$OUTPWS
2222 if [[ -n $OUTPWS ]]; then
2223 if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
2224 # Remove everything between '://' and '@'
2225 OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
2226 fi
2227 fi
2229 if [[ -z $HG_BRANCH ]]; then
2230 HG_BRANCH=`hg branch`
2231 if [ "$HG_BRANCH" == "default" ]; then
2232 #
2233 # 'default' means no particular branch, so let's cancel that
2234 #
2235 HG_BRANCH=
2236 fi
2237 fi
2239 if [[ -z $forestflag ]]; then
2240 if [[ -z $Nflag ]]; then
2241 #
2242 # If no "-N", always do "hg outgoing" against parent
2243 # repository to determine list of outgoing revisions.
2244 #
2245 ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
2246 if [[ -n $ALL_CREV ]]; then
2247 FIRST_CREV=`echo "$ALL_CREV" | head -1`
2248 #
2249 # If no "-r", choose revision to compare against by
2250 # finding the latest revision not in the outgoing list.
2251 #
2252 if [[ -z $rflag ]]; then
2253 OUTREV=`find_outrev "$FIRST_CREV"`
2254 if [[ -n $OUTREV ]]; then
2255 HG_LIST_FROM_COMMIT=1
2256 fi
2257 fi
2258 fi
2259 elif [[ -n $rflag ]]; then
2260 #
2261 # If skipping "hg outgoing" but still comparing against a
2262 # specific revision (not the tip), set revision for comment
2263 # accumulation.
2264 #
2265 FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
2266 FIRST_CREV=`expr $FIRST_CREV + 1`
2267 fi
2268 fi
2269 #Let's check if a merge is needed, if so, issue a warning
2270 PREV=`hg parent | grep '^tag:.*tip$'`
2271 if [[ -z $PREV ]]; then
2272 print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
2273 fi
2274 fi
2276 if [[ -n $lflag ]]; then
2277 #
2278 # If the -l flag is given instead of the name of a file list,
2279 # then generate the file list by extracting file names from a
2280 # putback -n.
2281 #
2282 shift $(($OPTIND - 1))
2283 if [[ $SCM_MODE == "teamware" ]]; then
2284 flist_from_teamware "$*"
2285 elif [[ $SCM_MODE == "mercurial" ]]; then
2286 flist_from_mercurial
2287 fi
2288 flist_done=1
2289 shift $#
2291 elif [[ -n $wflag ]]; then
2292 #
2293 # If the -w is given then assume the file list is in Bonwick's "wx"
2294 # command format, i.e. pathname lines alternating with SCCS comment
2295 # lines with blank lines as separators. Use the SCCS comments later
2296 # in building the index.html file.
2297 #
2298 shift $(($OPTIND - 1))
2299 wxfile=$1
2300 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2301 if [[ -r $CODEMGR_WS/wx/active ]]; then
2302 wxfile=$CODEMGR_WS/wx/active
2303 fi
2304 fi
2306 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
2307 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2309 print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2310 flist_from_wx $wxfile
2311 flist_done=1
2312 if [[ -n "$*" ]]; then
2313 shift
2314 fi
2315 elif [[ $flist_mode == "stdin" ]]; then
2316 print -u2 " File list from: standard input"
2317 elif [[ $flist_mode == "file" ]]; then
2318 print -u2 " File list from: $flist_file"
2319 fi
2321 if [[ $# -gt 0 ]]; then
2322 print -u2 "WARNING: unused arguments: $*"
2323 fi
2325 if [[ $SCM_MODE == "teamware" ]]; then
2326 #
2327 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
2328 # be set in a number of ways, in decreasing precedence:
2329 #
2330 # 1) on the command line (only for the parent)
2331 # 2) in the user environment
2332 # 3) in the flist
2333 # 4) automatically based on the workspace (only for the parent)
2334 #
2336 #
2337 # Here is case (2): the user environment
2338 #
2339 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
2340 [[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
2342 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
2343 print -u2 "$codemgr_ws: no such workspace"
2344 exit 1
2345 fi
2347 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
2348 codemgr_parent=$CODEMGR_PARENT
2350 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
2351 print -u2 "$codemgr_parent: no such directory"
2352 exit 1
2353 fi
2355 #
2356 # If we're in auto-detect mode and we haven't already gotten the file
2357 # list, then see if we can get it by probing for wx.
2358 #
2359 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
2360 if [[ ! -x $WX ]]; then
2361 print -u2 "WARNING: wx not found!"
2362 fi
2364 #
2365 # We need to use wx list -w so that we get renamed files, etc.
2366 # but only if a wx active file exists-- otherwise wx will
2367 # hang asking us to initialize our wx information.
2368 #
2369 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
2370 print -u2 " File list from: 'wx list -w' ... \c"
2371 $WX list -w > $FLIST
2372 $WX comments > /tmp/$$.wx_comments
2373 wxfile=/tmp/$$.wx_comments
2374 print -u2 "done"
2375 flist_done=1
2376 fi
2377 fi
2379 #
2380 # If by hook or by crook we've gotten a file list by now (perhaps
2381 # from the command line), eval it to extract environment variables from
2382 # it: This is step (3).
2383 #
2384 env_from_flist
2386 #
2387 # Continuing step (3): If we still have no file list, we'll try to get
2388 # it from teamware.
2389 #
2390 if [[ -z $flist_done ]]; then
2391 flist_from_teamware
2392 env_from_flist
2393 fi
2395 if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
2396 codemgr_ws=$PWD
2397 fi
2398 #
2399 # Observe true directory name of CODEMGR_WS, as used later in
2400 # webrev title.
2401 #
2402 if [[ -n $codemgr_ws ]]; then
2403 codemgr_ws=$(cd $codemgr_ws;print $PWD)
2404 fi
2406 if [[ -n $codemgr_parent ]]; then
2407 codemgr_parent=$(cd $codemgr_parent;print $PWD)
2408 fi
2410 #
2411 # (4) If we still don't have a value for codemgr_parent, get it
2412 # from workspace.
2413 #
2414 [[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
2415 [[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
2417 if [[ ! -d $codemgr_parent ]]; then
2418 print -u2 "$CODEMGR_PARENT: no such parent workspace"
2419 exit 1
2420 fi
2422 #
2423 # Reset CODEMGR_WS to make sure teamware commands are happy.
2424 #
2425 CODEMGR_WS=$codemgr_ws
2426 CWS=$codemgr_ws
2427 PWS=$codemgr_parent
2428 elif [[ $SCM_MODE == "mercurial" ]]; then
2429 if [[ -z $flist_done ]]; then
2430 flist_from_mercurial $PWS
2431 fi
2432 fi
2434 #
2435 # If the user didn't specify a -i option, check to see if there is a
2436 # webrev-info file in the workspace directory.
2437 #
2438 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2439 iflag=1
2440 INCLUDE_FILE="$CWS/webrev-info"
2441 fi
2443 if [[ -n $iflag ]]; then
2444 if [[ ! -r $INCLUDE_FILE ]]; then
2445 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
2446 "not readable."
2447 exit 1
2448 else
2449 #
2450 # $INCLUDE_FILE may be a relative path, and the script alters
2451 # PWD, so we just stash a copy in /tmp.
2452 #
2453 cp $INCLUDE_FILE /tmp/$$.include
2454 fi
2455 fi
2457 #
2458 # Output directory.
2459 #
2460 if [[ -z $WDIR ]]; then
2461 WDIR=$CWS/webrev
2462 else
2463 # If the output directory doesn't end with '/webrev' or '/webrev/'
2464 # then add '/webrev'. This is for backward compatibility
2465 if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
2466 then
2467 WDIR=$WDIR/webrev
2468 fi
2469 fi
2470 # WDIR=${WDIR:-$CWS/webrev}
2472 #
2473 # Name of the webrev, derived from the workspace name; in the
2474 # future this could potentially be an option.
2475 #
2476 # Let's keep what's after the last '/'
2477 WNAME=${CWS##*/}
2479 #
2480 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
2481 #
2482 if [ ${WDIR%%/*} ]; then
2483 if [[ -n $ISWIN ]]; then
2484 if [ ${WDIR%%[A-Za-z]:*} ]; then
2485 WDIR=$PWD/$WDIR
2486 fi
2487 else
2488 WDIR=$PWD/$WDIR
2489 fi
2490 fi
2492 if [[ ! -d $WDIR ]]; then
2493 mkdir -p $WDIR
2494 [[ $? != 0 ]] && exit 1
2495 fi
2497 #
2498 # Summarize what we're going to do.
2499 #
2500 print " Workspace: $CWS"
2501 if [[ -n $parent_webrev ]]; then
2502 print "Compare against: webrev at $parent_webrev"
2503 elif [[ -n $OUTPWS2 ]]; then
2504 print "Compare against: $OUTPWS2"
2505 fi
2506 if [[ -n $HG_BRANCH ]]; then
2507 print " Branch: $HG_BRANCH"
2508 fi
2509 if [[ -n $rflag ]]; then
2510 print "Compare against version: $PARENT_REV"
2511 fi
2512 [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE"
2513 print " Output to: $WDIR"
2515 #
2516 # Save the file list in the webrev dir
2517 #
2518 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
2520 #
2521 # Bug IDs will be replaced by a URL. Order of precedence
2522 # is: default location, $WEBREV_BUGURL, the -O flag.
2523 #
2524 BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr='
2525 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
2526 [[ -n "$Oflag" ]] && \
2527 BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
2529 #
2530 # Likewise, ARC cases will be replaced by a URL. Order of precedence
2531 # is: default, $WEBREV_SACURL, the -O flag.
2532 #
2533 # Note that -O also triggers different substitution behavior for
2534 # SACURL. See sac2url().
2535 #
2536 SACURL='http://sac.eng.sun.com'
2537 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
2538 [[ -n $Oflag ]] && \
2539 SACURL='http://www.opensolaris.org/os/community/arc/caselog'
2541 rm -f $WDIR/$WNAME.patch
2542 rm -f $WDIR/$WNAME.ps
2543 rm -f $WDIR/$WNAME.pdf
2545 touch $WDIR/$WNAME.patch
2547 print " Output Files:"
2549 #
2550 # Clean up the file list: Remove comments, blank lines and env variables.
2551 #
2552 sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean
2553 FLIST=/tmp/$$.flist.clean
2555 #
2556 # Clean up residual raw files
2557 #
2558 if [ -d $WDIR/raw_files ]; then
2559 rm -rf $WDIR/raw_files 2>/dev/null
2560 fi
2562 #
2563 # Should we ignore changes in white spaces when generating diffs?
2564 #
2565 if [[ -n $bflag ]]; then
2566 DIFFOPTS="-t"
2567 else
2568 DIFFOPTS="-bt"
2569 fi
2570 #
2571 # First pass through the files: generate the per-file webrev HTML-files.
2572 #
2573 while read LINE
2574 do
2575 set - $LINE
2576 P=$1
2578 if [[ $1 == "Revision:" ]]; then
2579 OUTREV=$2
2580 continue
2581 fi
2582 #
2583 # Normally, each line in the file list is just a pathname of a
2584 # file that has been modified or created in the child. A file
2585 # that is renamed in the child workspace has two names on the
2586 # line: new name followed by the old name.
2587 #
2588 oldname=""
2589 oldpath=""
2590 rename=
2591 if [[ $# -eq 2 ]]; then
2592 PP=$2 # old filename
2593 oldname=" (was $PP)"
2594 oldpath="$PP"
2595 rename=1
2596 PDIR=${PP%/*}
2597 if [[ $PDIR == $PP ]]; then
2598 PDIR="." # File at root of workspace
2599 fi
2601 PF=${PP##*/}
2603 DIR=${P%/*}
2604 if [[ $DIR == $P ]]; then
2605 DIR="." # File at root of workspace
2606 fi
2608 F=${P##*/}
2609 else
2610 DIR=${P%/*}
2611 if [[ "$DIR" == "$P" ]]; then
2612 DIR="." # File at root of workspace
2613 fi
2615 F=${P##*/}
2617 PP=$P
2618 PDIR=$DIR
2619 PF=$F
2620 fi
2622 # Make the webrev directory if necessary as it may have been
2623 # removed because it was empty
2624 if [ ! -d $CWS/$DIR ]; then
2625 mkdir -p $CWS/$DIR
2626 fi
2628 COMM=`getcomments html $P $PP`
2630 print "\t$P$oldname\n\t\t\c"
2632 # Make the webrev mirror directory if necessary
2633 mkdir -p $WDIR/$DIR
2635 # cd to the directory so the names are short
2636 cd $CWS/$DIR
2638 #
2639 # If we're in OpenSolaris mode, we enforce a minor policy:
2640 # help to make sure the reviewer doesn't accidentally publish
2641 # source which is in usr/closed/*
2642 #
2643 if [[ -n $Oflag ]]; then
2644 pclosed=${P##usr/closed/}
2645 if [[ $pclosed != $P ]]; then
2646 print "*** Omitting closed source for OpenSolaris" \
2647 "mode review"
2648 continue
2649 fi
2650 fi
2652 #
2653 # We stash old and new files into parallel directories in /tmp
2654 # and do our diffs there. This makes it possible to generate
2655 # clean looking diffs which don't have absolute paths present.
2656 #
2657 olddir=$WDIR/raw_files/old
2658 newdir=$WDIR/raw_files/new
2659 mkdir -p $olddir
2660 mkdir -p $newdir
2661 mkdir -p $olddir/$PDIR
2662 mkdir -p $newdir/$DIR
2664 build_old_new $olddir $newdir $DIR $F
2666 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
2667 print "*** Error: file not in parent or child"
2668 continue
2669 fi
2671 cd $WDIR/raw_files
2672 ofile=old/$PDIR/$PF
2673 nfile=new/$DIR/$F
2675 mv_but_nodiff=
2676 cmp $ofile $nfile > /dev/null 2>&1
2677 if [[ $? == 0 && $rename == 1 ]]; then
2678 mv_but_nodiff=1
2679 fi
2681 #
2682 # Cleaning up
2683 #
2684 rm -f $WDIR/$DIR/$F.cdiff.html
2685 rm -f $WDIR/$DIR/$F.udiff.html
2686 rm -f $WDIR/$DIR/$F.wdiff.html
2687 rm -f $WDIR/$DIR/$F.sdiff.html
2688 rm -f $WDIR/$DIR/$F-.html
2689 rm -f $WDIR/$DIR/$F.html
2691 its_a_jar=
2692 if expr $F : '.*\.jar' >/dev/null; then
2693 its_a_jar=1
2694 # It's a JAR file, let's do it differntly
2695 if [[ -z $JAR ]]; then
2696 print "No access to jar, so can't produce diffs for jar files"
2697 else
2698 if [ -f $ofile ]; then
2699 $JAR -tvf $ofile >"$ofile".lst
2700 fi
2701 if [ -f $nfile ]; then
2702 $JAR -tvf $nfile >"$nfile".lst
2703 fi
2705 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2707 ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
2708 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2709 > $WDIR/$DIR/$F.cdiff.html
2710 print " cdiffs\c"
2712 ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
2713 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2714 > $WDIR/$DIR/$F.udiff.html
2716 print " udiffs\c"
2718 if [[ -x $WDIFF ]]; then
2719 $WDIFF -c "$COMM" \
2720 -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
2721 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2722 if [[ $? -eq 0 ]]; then
2723 print " wdiffs\c"
2724 else
2725 print " wdiffs[fail]\c"
2726 fi
2727 fi
2729 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2730 > $WDIR/$DIR/$F.sdiff.html
2731 print " sdiffs\c"
2733 print " frames\c"
2735 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2737 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
2739 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2740 # renamed file: may also have differences
2741 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
2742 elif [[ -f $nfile ]]; then
2743 # new file: count added lines
2744 difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
2745 elif [[ -f $ofile ]]; then
2746 # old file: count deleted lines
2747 difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
2748 fi
2749 fi
2750 else
2752 #
2753 # If we have old and new versions of the file then run the
2754 # appropriate diffs. This is complicated by a couple of factors:
2755 #
2756 # - renames must be handled specially: we emit a 'remove'
2757 # diff and an 'add' diff
2758 # - new files and deleted files must be handled specially
2759 # - Solaris patch(1m) can't cope with file creation
2760 # (and hence renames) as of this writing.
2761 # - To make matters worse, gnu patch doesn't interpret the
2762 # output of Solaris diff properly when it comes to
2763 # adds and deletes. We need to do some "cleansing"
2764 # transformations:
2765 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@
2766 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@
2767 #
2768 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
2769 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
2771 rm -f $WDIR/$DIR/$F.patch
2772 if [[ -z $rename ]]; then
2773 if [ ! -f $ofile ]; then
2774 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2775 > $WDIR/$DIR/$F.patch
2776 elif [ ! -f $nfile ]; then
2777 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2778 > $WDIR/$DIR/$F.patch
2779 else
2780 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
2781 fi
2782 else
2783 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
2784 > $WDIR/$DIR/$F.patch
2786 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2787 >> $WDIR/$DIR/$F.patch
2789 fi
2792 #
2793 # Tack the patch we just made onto the accumulated patch for the
2794 # whole wad.
2795 #
2796 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
2798 print " patch\c"
2800 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2802 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
2803 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2804 > $WDIR/$DIR/$F.cdiff.html
2805 print " cdiffs\c"
2807 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
2808 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2809 > $WDIR/$DIR/$F.udiff.html
2811 print " udiffs\c"
2813 if [[ -x $WDIFF ]]; then
2814 $WDIFF -c "$COMM" \
2815 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
2816 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2817 if [[ $? -eq 0 ]]; then
2818 print " wdiffs\c"
2819 else
2820 print " wdiffs[fail]\c"
2821 fi
2822 fi
2824 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2825 > $WDIR/$DIR/$F.sdiff.html
2826 print " sdiffs\c"
2828 print " frames\c"
2830 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2832 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2834 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2835 # renamed file: may also have differences
2836 difflines $ofile $nfile > $WDIR/$DIR/$F.count
2837 elif [[ -f $nfile ]]; then
2838 # new file: count added lines
2839 difflines /dev/null $nfile > $WDIR/$DIR/$F.count
2840 elif [[ -f $ofile ]]; then
2841 # old file: count deleted lines
2842 difflines $ofile /dev/null > $WDIR/$DIR/$F.count
2843 fi
2844 fi
2845 #
2846 # Now we generate the postscript for this file. We generate diffs
2847 # only in the event that there is delta, or the file is new (it seems
2848 # tree-killing to print out the contents of deleted files).
2849 #
2850 if [[ -f $nfile ]]; then
2851 ocr=$ofile
2852 [[ ! -f $ofile ]] && ocr=/dev/null
2854 if [[ -z $mv_but_nodiff ]]; then
2855 textcomm=`getcomments text $P $PP`
2856 if [[ -x $CODEREVIEW ]]; then
2857 $CODEREVIEW -y "$textcomm" \
2858 -e $ocr $nfile \
2859 > /tmp/$$.psfile 2>/dev/null &&
2860 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
2861 if [[ $? -eq 0 ]]; then
2862 print " ps\c"
2863 else
2864 print " ps[fail]\c"
2865 fi
2866 fi
2867 fi
2868 fi
2870 if [[ -f $ofile && -z $mv_but_nodiff ]]; then
2871 if [[ -n $its_a_jar ]]; then
2872 source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
2873 else
2874 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
2875 fi
2876 print " old\c"
2877 fi
2879 if [[ -f $nfile ]]; then
2880 if [[ -n $its_a_jar ]]; then
2881 source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
2882 else
2883 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
2884 fi
2885 print " new\c"
2886 fi
2888 print
2889 done < $FLIST
2891 frame_nav_js > $WDIR/ancnav.js
2892 frame_navigation > $WDIR/ancnav.html
2894 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
2895 print " Generating PDF: \c"
2896 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
2897 print "Done."
2898 fi
2900 # Now build the index.html file that contains
2901 # links to the source files and their diffs.
2903 cd $CWS
2905 # Save total changed lines for Code Inspection.
2906 print "$TOTL" > $WDIR/TotalChangedLines
2908 print " index.html: \c"
2909 INDEXFILE=$WDIR/index.html
2910 exec 3<&1 # duplicate stdout to FD3.
2911 exec 1<&- # Close stdout.
2912 exec > $INDEXFILE # Open stdout to index file.
2914 print "$HTML<head>"
2915 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
2916 print "$STDHEAD"
2917 print "<title>$WNAME</title>"
2918 print "</head>"
2919 print "<body id=\"SUNWwebrev\">"
2920 print "<div class=\"summary\">"
2921 print "<h2>Code Review for $WNAME</h2>"
2923 print "<table>"
2925 if [[ -z $uflag ]]
2926 then
2927 if [[ $SCM_MODE == "mercurial" ]]
2928 then
2929 #
2930 # Let's try to extract the user name from the .hgrc file
2931 #
2932 username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
2933 fi
2935 if [[ -z $username ]]
2936 then
2937 #
2938 # Figure out the username and gcos name. To maintain compatibility
2939 # with passwd(4), we must support '&' substitutions.
2940 #
2941 username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
2942 if [[ -x $GETENT ]]; then
2943 realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
2944 fi
2945 userupper=`print "$username" | sed 's/\<./\u&/g'`
2946 realname=`print $realname | sed s/\&/$userupper/`
2947 fi
2948 fi
2950 date="on `date`"
2952 if [[ -n "$username" && -n "$realname" ]]; then
2953 print "<tr><th>Prepared by:</th>"
2954 print "<td>$realname ($username) $date</td></tr>"
2955 elif [[ -n "$username" ]]; then
2956 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
2957 fi
2959 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
2960 if [[ -n $parent_webrev ]]; then
2961 print "<tr><th>Compare against:</th><td>"
2962 print "webrev at $parent_webrev"
2963 else
2964 if [[ -n $OUTPWS2 ]]; then
2965 print "<tr><th>Compare against:</th><td>"
2966 print "$OUTPWS2"
2967 fi
2968 fi
2969 print "</td></tr>"
2970 if [[ -n $rflag ]]; then
2971 print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
2972 elif [[ -n $OUTREV ]]; then
2973 if [[ -z $forestflag ]]; then
2974 print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
2975 fi
2976 fi
2977 if [[ -n $HG_BRANCH ]]; then
2978 print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
2979 fi
2981 print "<tr><th>Summary of changes:</th><td>"
2982 printCI $TOTL $TINS $TDEL $TMOD $TUNC
2983 print "</td></tr>"
2985 if [[ -f $WDIR/$WNAME.patch ]]; then
2986 print "<tr><th>Patch of changes:</th><td>"
2987 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
2988 fi
2989 if [[ -f $WDIR/$WNAME.pdf ]]; then
2990 print "<tr><th>Printable review:</th><td>"
2991 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
2992 fi
2994 if [[ -n "$iflag" ]]; then
2995 print "<tr><th>Author comments:</th><td><div>"
2996 cat /tmp/$$.include
2997 print "</div></td></tr>"
2998 fi
2999 # Add links to referenced CRs, if any
3000 # external URL has a <title> like:
3001 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
3002 # while internal URL has <title> like:
3003 # <title>6641309: Wrong Cookie separator used in HttpURLConnection</title>
3004 #
3005 if [[ -n $CRID ]]; then
3006 for id in $CRID
3007 do
3008 print "<tr><th>Bug id:</th><td>"
3009 url="${BUGURL}${id}"
3010 if [[ -n $WGET ]]; then
3011 msg=`$WGET -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed 's/Bug ID://'`
3012 fi
3013 if [[ -n $msg ]]; then
3014 print "<a href=\"$url\">$msg</a>"
3015 else
3016 print $id | bug2url
3017 fi
3019 print "</td></tr>"
3020 done
3021 fi
3022 print "<tr><th>Legend:</th><td>"
3023 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>"
3024 print "</table>"
3025 print "</div>"
3027 #
3028 # Second pass through the files: generate the rest of the index file
3029 #
3030 while read LINE
3031 do
3032 set - $LINE
3033 if [[ $1 == "Revision:" ]]; then
3034 FIRST_CREV=`expr $3 + 1`
3035 continue
3036 fi
3037 P=$1
3039 if [[ $# == 2 ]]; then
3040 PP=$2
3041 oldname=" <i>(was $PP)</i>"
3043 else
3044 PP=$P
3045 oldname=""
3046 fi
3048 DIR=${P%/*}
3049 if [[ $DIR == $P ]]; then
3050 DIR="." # File at root of workspace
3051 fi
3053 # Avoid processing the same file twice.
3054 # It's possible for renamed files to
3055 # appear twice in the file list
3057 F=$WDIR/$P
3059 print "<p><code>"
3061 # If there's a diffs file, make diffs links
3063 NODIFFS=
3064 if [[ -f $F.cdiff.html ]]; then
3065 print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
3066 print "<a href=\"$P.udiff.html\">Udiffs</a>"
3068 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
3069 print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
3070 fi
3072 print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
3074 print "<a href=\"$P.frames.html\">Frames</a>"
3075 else
3076 NODIFFS=1
3077 print " ------ ------ ------"
3079 if [[ -x $WDIFF ]]; then
3080 print " ------"
3081 fi
3083 print " ------"
3084 fi
3086 # If there's an old file, make the link
3088 NOOLD=
3089 if [[ -f $F-.html ]]; then
3090 print "<a href=\"$P-.html\">Old</a>"
3091 else
3092 NOOLD=1
3093 print " ---"
3094 fi
3096 # If there's an new file, make the link
3098 NONEW=
3099 if [[ -f $F.html ]]; then
3100 print "<a href=\"$P.html\">New</a>"
3101 else
3102 NONEW=1
3103 print " ---"
3104 fi
3106 if [[ -f $F.patch ]]; then
3107 print "<a href=\"$P.patch\">Patch</a>"
3108 else
3109 print " -----"
3110 fi
3112 if [[ -f $WDIR/raw_files/new/$P ]]; then
3113 print "<a href=\"raw_files/new/$P\">Raw</a>"
3114 else
3115 print " ---"
3116 fi
3117 print "</code>"
3118 if [[ -n $NODIFFS && -z $oldname ]]; then
3119 if [[ -n $NOOLD ]]; then
3120 print "<font color=green><b>$P</b></font>"
3121 elif [[ -n $NONEW ]]; then
3122 print "<font color=red><b>$P</b></font>"
3123 fi
3124 else
3125 print "<b>$P</b> $oldname"
3126 fi
3128 #
3129 # Check for usr/closed
3130 #
3131 if [ ! -z "$Oflag" ]; then
3132 if [[ $P == usr/closed/* ]]; then
3133 print " <i>Closed source: omitted from" \
3134 "this review</i>"
3135 fi
3136 fi
3138 print "</p><blockquote>\c"
3139 # Insert delta comments if any
3140 comments=`getcomments html $P $PP`
3141 if [ -n "$comments" ]; then
3142 print "<pre>$comments</pre>"
3143 fi
3145 # Add additional comments comment
3147 print "<!-- Add comments to explain changes in $P here -->"
3149 # Add count of changes.
3151 if [[ -f $F.count ]]; then
3152 cat $F.count
3153 rm $F.count
3154 fi
3155 print "</blockquote>"
3156 done < $FLIST
3158 print
3159 print
3160 print "<hr />"
3161 print "<p style=\"font-size: small\">"
3162 print "This code review page was prepared using <b>$0</b>"
3163 print "(vers $WEBREV_UPDATED)."
3164 print "</body>"
3165 print "</html>"
3167 if [[ -n $ZIP ]]; then
3168 # Let's generate a zip file for convenience
3169 cd $WDIR/..
3170 if [ -f webrev.zip ]; then
3171 rm webrev.zip
3172 fi
3173 $ZIP -r webrev webrev >/dev/null 2>&1
3174 fi
3176 exec 1<&- # Close FD 1.
3177 exec 1<&3 # dup FD 3 to restore stdout.
3178 exec 3<&- # close FD 3.
3180 print "Done."
3181 print "Output to: $WDIR"