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 # 29 30 WEBREV_UPDATED=23.18-hg 31 32 HTML='<?xml version="1.0"?> 33 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 35 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' 36 37 FRAMEHTML='<?xml version="1.0"?> 38 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" 39 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> 40 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' 41 42 STDHEAD='<meta 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. 49 50 For example, to have all "removed" information be red instead of 51 brown, set a rule in your userContent.css file like: 52 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; } 102 103 </style> 104 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 ' 116 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 ' 129 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 } 141 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 } 151 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 } 168 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 } 198 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>" } 215 216 ' $1 217 } 218 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 283 284 TNAME=$3 285 TPATH=$4 286 COMMENT=$5 287 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} 303 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 } 316 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" ; 362 363 next 364 } 365 } 366 367 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" } 368 ' /tmp/$$.diffs > /tmp/$$.file1 369 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 } 382 383 /^</ {next} 384 /^>/ {next} 385 /^---/ {next} 386 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 } 399 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 451 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 456 457 html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html 458 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>" 470 471 strip_unchanged /tmp/$$.file1.html 472 473 print "</pre></td><td><pre>" 474 475 strip_unchanged /tmp/$$.file2.html 476 477 print "</pre></td>" 478 print "</tr></table>" 479 print "</body></html>" 480 481 framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \ 482 "$COMMENT" 483 } 484 485 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 507 508 # Enable html files to access WDIR via a relative path. 509 RTOP=$(relative_dir $TPATH $WDIR) 510 511 # Make the rhs/lhs files and output the frameset file. 512 print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html 513 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 521 522 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html 523 524 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html 525 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html 526 527 close='</body></html>' 528 529 print $close >> $WDIR/$DIR/$TNAME.lhs.html 530 print $close >> $WDIR/$DIR/$TNAME.rhs.html 531 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 } 553 554 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 569 570 cat > /tmp/$$.crmerge.pl << \EOF 571 572 print scalar(<>); # %!PS-Adobe--- 573 print "%%Orientation: Landscape\n"; 574 575 $pno = 0; 576 $doprint = 1; 577 578 $page = ""; 579 580 while (<>) { 581 next if (/^%%Pages:\s*\d+/); 582 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 } 596 597 # Skip from %%Trailer of one document to Endprolog 598 # %%Page of the next 599 $doprint = 0 if (/^%%Trailer/); 600 $page .= $_ if ($doprint); 601 } 602 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 611 612 $PERL /tmp/$$.crmerge.pl < $infile 613 } 614 615 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 } 640 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(); 660 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 } 672 673 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 702 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 } 715 716 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; 729 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 } 739 740 function scrollToAnc(num) { 741 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); 747 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); 757 758 parent.lhs.scrollBy(0,-30); 759 parent.rhs.scrollBy(0,-30); 760 } 761 762 function getAncValue() 763 { 764 return (parseInt(parent.nav.document.diff.real.value)); 765 } 766 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 } 775 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 } 787 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 } 794 795 function stopScroll() { 796 if (scrolling==1) { 797 clearInterval(myInt); 798 scrolling=0; 799 } 800 } 801 802 function startScroll() { 803 stopScroll(); 804 scrolling=1; 805 myInt=setInterval("scrollByPix()",10); 806 } 807 808 function handlePress(b) { 809 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 } 833 834 function handleRelease(b) { 835 stopScroll(); 836 } 837 838 function keypress(ev) { 839 var keynum; 840 var keychar; 841 842 if (window.event) { // IE 843 keynum = ev.keyCode; 844 } else if (ev.which) { // non-IE 845 keynum = ev.which; 846 } 847 848 keychar = String.fromCharCode(keynum); 849 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 } 859 860 function ValidateDiffNum(){ 861 val = parent.nav.document.diff.display.value; 862 if (val == "EOF") { 863 scrollToAnc(999999); 864 return; 865 } 866 867 if (val == "BOF") { 868 scrollToAnc(0); 869 return; 870 } 871 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 } 880 881 EOF 882 } 883 884 # 885 # frame_navigation 886 # 887 # Output anchor navigation file for framed sdiffs. 888 # 889 function frame_navigation 890 { 891 print "$HTML<head>$STDHEAD" 892 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" /> 897 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 906 907 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>" 908 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> 947 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 } 983 984 985 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 998 999 print "$HTML<head>$STDHEAD" 1000 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>" 1001 1002 if [[ $DIFFTYPE == "U" ]]; then 1003 print "$UDIFFCSS" 1004 fi 1005 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 1014 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} 1025 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 ' 1035 1036 print "</pre></body></html>\n" 1037 } 1038 1039 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 1049 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 } 1057 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 1073 1074 psid=$($SCCS prs -d:I: $pfile 2>/dev/null) 1075 if [[ -z "$psid" ]]; then 1076 psid=1.1 1077 fi 1078 1079 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null) 1080 N=${#sids[@]} 1081 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 }' 1087 1088 if [[ $N -ge 2 ]]; then 1089 sid1=${sids[$((N-2))]} # Gets 2nd to last sid 1090 1091 if [[ $fmt == "text" ]]; then 1092 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1093 $AWK "$nawkprg" 1094 return 1095 fi 1096 1097 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1098 html_quote | bug2url | sac2url | $AWK "$nawkprg" 1099 fi 1100 } 1101 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 1114 1115 comm=`$AWK ' 1116 $1 == "'$p'" { 1117 do getline ; while (NF > 0) 1118 getline 1119 while (NF > 0) { print ; getline } 1120 exit 1121 }' < $wxfile` 1122 1123 if [[ $fmt == "text" ]]; then 1124 print "$comm" 1125 return 1126 fi 1127 1128 print "$comm" | html_quote | bug2url | sac2url 1129 } 1130 1131 comments_from_mercurial() 1132 { 1133 fmt=$1 1134 pfile=$PWS/$2 1135 cfile=$CWS/$3 1136 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 1163 1164 print "$comm" | html_quote | bug2url | sac2url 1165 ) 1166 fi 1167 } 1168 1169 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 1180 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 } 1191 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 } 1209 1210 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 1221 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; 1235 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 } 1254 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 } 1264 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 } 1274 1275 # Add lines: both Na and N,Na 1276 /^[0-9].*a$/ { 1277 while (getline != /^\.$/) a++; 1278 next; 1279 } 1280 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 } 1296 1297 # Delete line: Nd. For example 10d says line 10 is deleted. 1298 /^[0-9]*d$/ {d++; next} 1299 1300 # Should not get here! 1301 { 1302 error=1; 1303 exit; 1304 } 1305 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 }' ) 1311 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 1318 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 } 1335 1336 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 1355 1356 $AWK '{ c = 1; print; 1357 while (getline) { 1358 if (NF == 0) { c = -c; continue } 1359 if (c > 0) print 1360 } 1361 }' $wxfile > $FLIST 1362 1363 print " Done." 1364 } 1365 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 1385 1386 print " File list from: 'putback -n $parent_args $*' ... \c" 1387 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 1400 1401 print " Done." 1402 } 1403 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 } 1434 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 } 1453 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" 1462 1463 $HGCMD -mdn 2>/dev/null | $FILTER | while read F 1464 do 1465 echo $TREE/$F 1466 done >> $FLIST 1467 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 1478 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 } 1508 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 1524 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 # 1539 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 } 1593 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. 1606 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 1630 1631 hg status $STATUS_REV -mdn | $FILTER > $FLIST 1632 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 1643 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 } 1677 1678 function env_from_flist 1679 { 1680 [[ -r $FLIST ]] || return 1681 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 = ` 1688 1689 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1690 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 } 1697 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 1716 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 1725 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 } 1736 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 } 1746 1747 function look_for_prog 1748 { 1749 typeset path 1750 typeset ppath 1751 typeset progname=$1 1752 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 1760 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 1766 1767 PATH=$ppath prog=`whence $progname` 1768 if [[ -n $prog ]]; then 1769 print $prog 1770 fi 1771 } 1772 1773 function build_old_new_teamware 1774 { 1775 # If the child's version doesn't exist then 1776 # get a readonly copy. 1777 1778 if [[ ! -f $F && -f SCCS/s.$F ]]; then 1779 $SCCS get -s $F 1780 fi 1781 1782 # 1783 # Snag new version of file. 1784 # 1785 rm -f $newdir/$DIR/$F 1786 cp $F $newdir/$DIR/$F 1787 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. 1808 1809 rm -f $olddir/$DIR/$F 1810 cp $PWS/$PDIR/$PF $olddir/$DIR/$F 1811 fi 1812 fi 1813 } 1814 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 } 1834 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 1847 1848 } 1849 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 1863 1864 # 1865 # Old version of the file. 1866 # 1867 rm -rf $olddir/$DIR/$F 1868 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 1884 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 1897 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 } 1931 1932 function build_old_new 1933 { 1934 if [[ $SCM_MODE == "teamware" ]]; then 1935 build_old_new_teamware $@ 1936 fi 1937 1938 if [[ $SCM_MODE == "mercurial" ]]; then 1939 build_old_new_mercurial $@ 1940 fi 1941 } 1942 1943 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'] 1953 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 1966 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 1971 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. 1976 1977 SCM Environment: 1978 Teamware: CODEMGR_WS: Workspace location. 1979 Teamware: CODEMGR_PARENT: Parent workspace location. 1980 1981 " 1982 1983 exit 2 1984 } 1985 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 1995 1996 set +o noclobber 1997 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` 2012 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 2021 2022 if [[ ! -x $PERL ]]; then 2023 print -u2 "Error: No perl interpreter found. Exiting." 2024 exit 1 2025 fi 2026 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." 2035 2036 # Declare global total counters. 2037 integer TOTL TINS TDEL TMOD TUNC 2038 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;; 2056 2057 i) iflag=1 2058 INCLUDE_FILE=$OPTARG;; 2059 2060 o) oflag=1 2061 WDIR=$OPTARG;; 2062 2063 p) pflag=1 2064 codemgr_parent=$OPTARG;; 2065 2066 u) uflag=1 2067 username=$OPTARG;; 2068 2069 c) if [[ -z $CRID ]]; then 2070 CRID=$OPTARG 2071 else 2072 CRID="$CRID $OPTARG" 2073 fi;; 2074 2075 m) SCM_MODE="mercurial";; 2076 2077 t) SCM_MODE="teamware";; 2078 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;; 2086 2087 w) wflag=1;; 2088 2089 O) Oflag=1;; 2090 2091 N) Nflag=1;; 2092 2093 f) forestflag=1;; 2094 2095 r) rflag=1 2096 PARENT_REV=$OPTARG;; 2097 2098 v) print "$0 version: $WEBREV_UPDATED";; 2099 2100 2101 ?) usage;; 2102 esac 2103 done 2104 2105 FLIST=/tmp/$$.flist 2106 2107 if [[ -n $wflag && -n $lflag ]]; then 2108 usage 2109 fi 2110 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 2115 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 2124 2125 if [[ -z $wflag && -z $lflag ]]; then 2126 shift $(($OPTIND - 1)) 2127 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 2147 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 2162 2163 print -u2 " SCM detected: $SCM_MODE" 2164 2165 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 2228 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 2238 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 2275 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 $# 2290 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 2305 2306 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \ 2307 "be auto-detected (check \$CODEMGR_WS)" && exit 1 2308 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 2320 2321 if [[ $# -gt 0 ]]; then 2322 print -u2 "WARNING: unused arguments: $*" 2323 fi 2324 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 # 2335 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` 2341 2342 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then 2343 print -u2 "$codemgr_ws: no such workspace" 2344 exit 1 2345 fi 2346 2347 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 2348 codemgr_parent=$CODEMGR_PARENT 2349 2350 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then 2351 print -u2 "$codemgr_parent: no such directory" 2352 exit 1 2353 fi 2354 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 2363 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 2378 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 2385 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 2394 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 2405 2406 if [[ -n $codemgr_parent ]]; then 2407 codemgr_parent=$(cd $codemgr_parent;print $PWD) 2408 fi 2409 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` 2416 2417 if [[ ! -d $codemgr_parent ]]; then 2418 print -u2 "$CODEMGR_PARENT: no such parent workspace" 2419 exit 1 2420 fi 2421 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 2433 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 2442 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 2456 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} 2471 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##*/} 2478 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 2491 2492 if [[ ! -d $WDIR ]]; then 2493 mkdir -p $WDIR 2494 [[ $? != 0 ]] && exit 1 2495 fi 2496 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" 2514 2515 # 2516 # Save the file list in the webrev dir 2517 # 2518 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list 2519 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=' 2528 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' 2540 2541 rm -f $WDIR/$WNAME.patch 2542 rm -f $WDIR/$WNAME.ps 2543 rm -f $WDIR/$WNAME.pdf 2544 2545 touch $WDIR/$WNAME.patch 2546 2547 print " Output Files:" 2548 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 2554 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 2561 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 2577 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 2600 2601 PF=${PP##*/} 2602 2603 DIR=${P%/*} 2604 if [[ $DIR == $P ]]; then 2605 DIR="." # File at root of workspace 2606 fi 2607 2608 F=${P##*/} 2609 else 2610 DIR=${P%/*} 2611 if [[ "$DIR" == "$P" ]]; then 2612 DIR="." # File at root of workspace 2613 fi 2614 2615 F=${P##*/} 2616 2617 PP=$P 2618 PDIR=$DIR 2619 PF=$F 2620 fi 2621 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 2627 2628 COMM=`getcomments html $P $PP` 2629 2630 print "\t$P$oldname\n\t\t\c" 2631 2632 # Make the webrev mirror directory if necessary 2633 mkdir -p $WDIR/$DIR 2634 2635 # cd to the directory so the names are short 2636 cd $CWS/$DIR 2637 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 2651 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 2663 2664 build_old_new $olddir $newdir $DIR $F 2665 2666 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then 2667 print "*** Error: file not in parent or child" 2668 continue 2669 fi 2670 2671 cd $WDIR/raw_files 2672 ofile=old/$PDIR/$PF 2673 nfile=new/$DIR/$F 2674 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 2680 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 2690 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 2704 2705 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2706 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" 2711 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 2715 2716 print " udiffs\c" 2717 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 2728 2729 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2730 > $WDIR/$DIR/$F.sdiff.html 2731 print " sdiffs\c" 2732 2733 print " frames\c" 2734 2735 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2736 2737 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count 2738 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 2751 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/'" 2770 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 2785 2786 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2787 >> $WDIR/$DIR/$F.patch 2788 2789 fi 2790 2791 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 2797 2798 print " patch\c" 2799 2800 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2801 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" 2806 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 2810 2811 print " udiffs\c" 2812 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 2823 2824 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2825 > $WDIR/$DIR/$F.sdiff.html 2826 print " sdiffs\c" 2827 2828 print " frames\c" 2829 2830 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2831 2832 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2833 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 2853 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 2869 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 2878 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 2887 2888 print 2889 done < $FLIST 2890 2891 frame_nav_js > $WDIR/ancnav.js 2892 frame_navigation > $WDIR/ancnav.html 2893 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 2899 2900 # Now build the index.html file that contains 2901 # links to the source files and their diffs. 2902 2903 cd $CWS 2904 2905 # Save total changed lines for Code Inspection. 2906 print "$TOTL" > $WDIR/TotalChangedLines 2907 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. 2913 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>" 2922 2923 print "<table>" 2924 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 2934 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 2949 2950 date="on `date`" 2951 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 2958 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 2980 2981 print "<tr><th>Summary of changes:</th><td>" 2982 printCI $TOTL $TINS $TDEL $TMOD $TUNC 2983 print "</td></tr>" 2984 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 2993 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 3018 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>" 3026 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 3038 3039 if [[ $# == 2 ]]; then 3040 PP=$2 3041 oldname=" <i>(was $PP)</i>" 3042 3043 else 3044 PP=$P 3045 oldname="" 3046 fi 3047 3048 DIR=${P%/*} 3049 if [[ $DIR == $P ]]; then 3050 DIR="." # File at root of workspace 3051 fi 3052 3053 # Avoid processing the same file twice. 3054 # It's possible for renamed files to 3055 # appear twice in the file list 3056 3057 F=$WDIR/$P 3058 3059 print "<p><code>" 3060 3061 # If there's a diffs file, make diffs links 3062 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>" 3067 3068 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 3069 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 3070 fi 3071 3072 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 3073 3074 print "<a href=\"$P.frames.html\">Frames</a>" 3075 else 3076 NODIFFS=1 3077 print " ------ ------ ------" 3078 3079 if [[ -x $WDIFF ]]; then 3080 print " ------" 3081 fi 3082 3083 print " ------" 3084 fi 3085 3086 # If there's an old file, make the link 3087 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 3095 3096 # If there's an new file, make the link 3097 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 3105 3106 if [[ -f $F.patch ]]; then 3107 print "<a href=\"$P.patch\">Patch</a>" 3108 else 3109 print " -----" 3110 fi 3111 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 3127 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 3137 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 3144 3145 # Add additional comments comment 3146 3147 print "<!-- Add comments to explain changes in $P here -->" 3148 3149 # Add count of changes. 3150 3151 if [[ -f $F.count ]]; then 3152 cat $F.count 3153 rm $F.count 3154 fi 3155 print "</blockquote>" 3156 done < $FLIST 3157 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>" 3166 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 3175 3176 exec 1<&- # Close FD 1. 3177 exec 1<&3 # dup FD 3 to restore stdout. 3178 exec 3<&- # close FD 3. 3179 3180 print "Done." 3181 print "Output to: $WDIR" 3182