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: #A52A2A; /*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 <title>$WNAME rhs/lhs "\ 513 "$TPATH/$TNAME</title>" > $WDIR/$DIR/$TNAME.lhs.html 514 515 cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF 516 <script type="text/javascript" src="$RTOP/ancnav.js"></script> 517 </head> 518 <body id="SUNWwebrev" onkeypress="keypress(event);"> 519 <a name="0"></a> 520 <pre>$comments</pre><hr></hr> 521 EOF 522 523 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html 524 525 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html 526 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html 527 528 close='</body></html>' 529 530 print $close >> $WDIR/$DIR/$TNAME.lhs.html 531 print $close >> $WDIR/$DIR/$TNAME.rhs.html 532 533 print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html 534 print "<title>$WNAME Framed-Sdiff " \ 535 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html 536 cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF 537 <frameset rows="*,60"> 538 <frameset cols="50%,50%"> 539 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs" /> 540 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs" /> 541 </frameset> 542 <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0" 543 marginheight="0" name="nav" /> 544 <noframes> 545 <body id="SUNWwebrev"> 546 Alas 'frames' webrev requires that your browser supports frames 547 and has the feature enabled. 548 </body> 549 </noframes> 550 </frameset> 551 </html> 552 EOF 553 } 554 555 556 # 557 # fix_postscript 558 # 559 # Merge codereview output files to a single conforming postscript file, by: 560 # - removing all extraneous headers/trailers 561 # - making the page numbers right 562 # - removing pages devoid of contents which confuse some 563 # postscript readers. 564 # 565 # From Casper. 566 # 567 function fix_postscript 568 { 569 infile=$1 570 571 cat > /tmp/$$.crmerge.pl << \EOF 572 573 print scalar(<>); # %!PS-Adobe--- 574 print "%%Orientation: Landscape\n"; 575 576 $pno = 0; 577 $doprint = 1; 578 579 $page = ""; 580 581 while (<>) { 582 next if (/^%%Pages:\s*\d+/); 583 584 if (/^%%Page:/) { 585 if ($pno == 0 || $page =~ /\)S/) { 586 # Header or single page containing text 587 print "%%Page: ? $pno\n" if ($pno > 0); 588 print $page; 589 $pno++; 590 } else { 591 # Empty page, skip it. 592 } 593 $page = ""; 594 $doprint = 1; 595 next; 596 } 597 598 # Skip from %%Trailer of one document to Endprolog 599 # %%Page of the next 600 $doprint = 0 if (/^%%Trailer/); 601 $page .= $_ if ($doprint); 602 } 603 604 if ($page =~ /\)S/) { 605 print "%%Page: ? $pno\n"; 606 print $page; 607 } else { 608 $pno--; 609 } 610 print "%%Trailer\n%%Pages: $pno\n"; 611 EOF 612 613 $PERL /tmp/$$.crmerge.pl < $infile 614 } 615 616 617 # 618 # input_cmd | insert_anchors | output_cmd 619 # 620 # Flag blocks of difference with sequentially numbered invisible 621 # anchors. These are used to drive the frames version of the 622 # sdiffs output. 623 # 624 # NOTE: Anchor zero flags the top of the file irrespective of changes, 625 # an additional anchor is also appended to flag the bottom. 626 # 627 # The script detects changed lines as any line that has a "<span 628 # class=" string embedded (unchanged lines have no class set and are 629 # not part of a <span>. Blank lines (without a sequence number) 630 # are also detected since they flag lines that have been inserted or 631 # deleted. 632 # 633 function insert_anchors 634 { 635 $AWK ' 636 function ia() { 637 # This should be able to be a singleton <a /> but that 638 # seems to trigger a bug in firefox a:hover rule processing 639 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++; 640 } 641 642 BEGIN { 643 anc=1; 644 inblock=1; 645 printf "<pre>\n"; 646 } 647 NF == 0 || /^<span class=/ { 648 if (inblock == 0) { 649 ia(); 650 inblock=1; 651 } 652 print; 653 next; 654 } 655 { 656 inblock=0; 657 print; 658 } 659 END { 660 ia(); 661 662 printf "<b style=\"font-size: large; color: red\">"; 663 printf "--- EOF ---</b>" 664 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n"; 665 printf "</pre>" 666 printf "<form name=\"eof\" action=\"#\">"; 667 printf "<input name=\"value\" value=\"%d\" type=\"hidden\" />", 668 anc - 1; 669 printf "</form>"; 670 } 671 ' $1 672 } 673 674 675 # 676 # relative_dir 677 # 678 # Print a relative return path from $1 to $2. For example if 679 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview, 680 # this function would print "../../../../". 681 # 682 # In the event that $1 is not in $2 a warning is printed to stderr, 683 # and $2 is returned-- the result of this is that the resulting webrev 684 # is not relocatable. 685 # 686 function relative_dir 687 { 688 d1=$1 689 d2=$2 690 if [[ "$d1" == "." ]]; then 691 print "." 692 else 693 typeset cur="${d1##$d2?(/)}" 694 typeset ret="" 695 if [[ $d2 == $cur ]]; then # Should never happen. 696 # Should never happen. 697 print -u2 "\nWARNING: relative_dir: \"$1\" not relative " 698 print -u2 "to \"$2\". Check input paths. Framed webrev " 699 print -u2 "will not be relocatable!" 700 print $2 701 return 702 fi 703 704 while [[ -n ${cur} ]]; 705 do 706 cur=${cur%%*(/)*([!/])} 707 if [[ -z $ret ]]; then 708 ret=".." 709 else 710 ret="../$ret" 711 fi 712 done 713 print $ret 714 fi 715 } 716 717 718 # 719 # frame_nav_js 720 # 721 # Emit javascript for frame navigation 722 # 723 function frame_nav_js 724 { 725 cat << \EOF 726 var myInt; 727 var scrolling=0; 728 var sfactor = 3; 729 var scount=10; 730 731 function scrollByPix() { 732 if (scount<=0) { 733 sfactor*=1.2; 734 scount=10; 735 } 736 parent.lhs.scrollBy(0,sfactor); 737 parent.rhs.scrollBy(0,sfactor); 738 scount--; 739 } 740 741 function scrollToAnc(num) { 742 743 // Update the value of the anchor in the form which we use as 744 // storage for this value. setAncValue() will take care of 745 // correcting for overflow and underflow of the value and return 746 // us the new value. 747 num = setAncValue(num); 748 749 // Set location and scroll back a little to expose previous 750 // lines. 751 // 752 // Note that this could be improved: it is possible although 753 // complex to compute the x and y position of an anchor, and to 754 // scroll to that location directly. 755 // 756 parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num); 757 parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num); 758 759 parent.lhs.scrollBy(0,-30); 760 parent.rhs.scrollBy(0,-30); 761 } 762 763 function getAncValue() 764 { 765 return (parseInt(parent.nav.document.diff.real.value)); 766 } 767 768 function setAncValue(val) 769 { 770 if (val <= 0) { 771 val = 0; 772 parent.nav.document.diff.real.value = val; 773 parent.nav.document.diff.display.value = "BOF"; 774 return (val); 775 } 776 777 // 778 // The way we compute the max anchor value is to stash it 779 // inline in the left and right hand side pages-- it's the same 780 // on each side, so we pluck from the left. 781 // 782 maxval = parent.lhs.document.eof.value.value; 783 if (val < maxval) { 784 parent.nav.document.diff.real.value = val; 785 parent.nav.document.diff.display.value = val.toString(); 786 return (val); 787 } 788 789 // this must be: val >= maxval 790 val = maxval; 791 parent.nav.document.diff.real.value = val; 792 parent.nav.document.diff.display.value = "EOF"; 793 return (val); 794 } 795 796 function stopScroll() { 797 if (scrolling==1) { 798 clearInterval(myInt); 799 scrolling=0; 800 } 801 } 802 803 function startScroll() { 804 stopScroll(); 805 scrolling=1; 806 myInt=setInterval("scrollByPix()",10); 807 } 808 809 function handlePress(b) { 810 811 switch (b) { 812 case 1 : 813 scrollToAnc(-1); 814 break; 815 case 2 : 816 scrollToAnc(getAncValue() - 1); 817 break; 818 case 3 : 819 sfactor=-3; 820 startScroll(); 821 break; 822 case 4 : 823 sfactor=3; 824 startScroll(); 825 break; 826 case 5 : 827 scrollToAnc(getAncValue() + 1); 828 break; 829 case 6 : 830 scrollToAnc(999999); 831 break; 832 } 833 } 834 835 function handleRelease(b) { 836 stopScroll(); 837 } 838 839 function keypress(ev) { 840 var keynum; 841 var keychar; 842 843 if (window.event) { // IE 844 keynum = ev.keyCode; 845 } else if (ev.which) { // non-IE 846 keynum = ev.which; 847 } 848 849 keychar = String.fromCharCode(keynum); 850 851 if (keychar == "k") { 852 handlePress(2); 853 return (0); 854 } else if (keychar == "j" || keychar == " ") { 855 handlePress(5); 856 return (0); 857 } 858 return (1); 859 } 860 861 function ValidateDiffNum(){ 862 val = parent.nav.document.diff.display.value; 863 if (val == "EOF") { 864 scrollToAnc(999999); 865 return; 866 } 867 868 if (val == "BOF") { 869 scrollToAnc(0); 870 return; 871 } 872 873 i=parseInt(val); 874 if (isNaN(i)) { 875 parent.nav.document.diff.display.value = getAncValue(); 876 } else { 877 scrollToAnc(i); 878 } 879 return false; 880 } 881 882 EOF 883 } 884 885 # 886 # frame_navigation 887 # 888 # Output anchor navigation file for framed sdiffs. 889 # 890 function frame_navigation 891 { 892 print "$HTML<head>$STDHEAD" 893 894 cat << \EOF 895 <title>Anchor Navigation</title> 896 <meta http-equiv="Content-Script-Type" content="text/javascript" /> 897 <meta http-equiv="Content-Type" content="text/html" /> 898 899 <style type="text/css"> 900 div.button td { padding-left: 5px; padding-right: 5px; 901 background-color: #eee; text-align: center; 902 border: 1px #444 outset; cursor: pointer; } 903 div.button a { font-weight: bold; color: black } 904 div.button td:hover { background: #ffcc99; } 905 </style> 906 EOF 907 908 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>" 909 910 cat << \EOF 911 </head> 912 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();" 913 onkeypress="keypress(event);"> 914 <noscript lang="javascript"> 915 <center> 916 <p><big>Framed Navigation controls require Javascript</big><br /> 917 Either this browser is incompatable or javascript is not enabled</p> 918 </center> 919 </noscript> 920 <table width="100%" border="0" align="center"> 921 <tr> 922 <td valign="middle" width="25%">Diff navigation: 923 Use 'j' and 'k' for next and previous diffs; or use buttons 924 at right</td> 925 <td align="center" valign="top" width="50%"> 926 <div class="button"> 927 <table border="0" align="center"> 928 <tr> 929 <td> 930 <a onmousedown="handlePress(1);return true;" 931 onmouseup="handleRelease(1);return true;" 932 onmouseout="handleRelease(1);return true;" 933 onclick="return false;" 934 title="Go to Beginning Of file">BOF</a></td> 935 <td> 936 <a onmousedown="handlePress(3);return true;" 937 onmouseup="handleRelease(3);return true;" 938 onmouseout="handleRelease(3);return true;" 939 title="Scroll Up: Press and Hold to accelerate" 940 onclick="return false;">Scroll Up</a></td> 941 <td> 942 <a onmousedown="handlePress(2);return true;" 943 onmouseup="handleRelease(2);return true;" 944 onmouseout="handleRelease(2);return true;" 945 title="Go to previous Diff" 946 onclick="return false;">Prev Diff</a> 947 </td></tr> 948 949 <tr> 950 <td> 951 <a onmousedown="handlePress(6);return true;" 952 onmouseup="handleRelease(6);return true;" 953 onmouseout="handleRelease(6);return true;" 954 onclick="return false;" 955 title="Go to End Of File">EOF</a></td> 956 <td> 957 <a onmousedown="handlePress(4);return true;" 958 onmouseup="handleRelease(4);return true;" 959 onmouseout="handleRelease(4);return true;" 960 title="Scroll Down: Press and Hold to accelerate" 961 onclick="return false;">Scroll Down</a></td> 962 <td> 963 <a onmousedown="handlePress(5);return true;" 964 onmouseup="handleRelease(5);return true;" 965 onmouseout="handleRelease(5);return true;" 966 title="Go to next Diff" 967 onclick="return false;">Next Diff</a></td> 968 </tr> 969 </table> 970 </div> 971 </td> 972 <th valign="middle" width="25%"> 973 <form action="" name="diff" onsubmit="return ValidateDiffNum();"> 974 <input name="display" value="BOF" size="8" type="text" /> 975 <input name="real" value="0" size="8" type="hidden" /> 976 </form> 977 </th> 978 </tr> 979 </table> 980 </body> 981 </html> 982 EOF 983 } 984 985 986 987 # 988 # diff_to_html <filename> <filepath> { U | C } <comment> 989 # 990 # Processes the output of diff to produce an HTML file representing either 991 # context or unified diffs. 992 # 993 diff_to_html() 994 { 995 TNAME=$1 996 TPATH=$2 997 DIFFTYPE=$3 998 COMMENT=$4 999 1000 print "$HTML<head>$STDHEAD" 1001 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>" 1002 1003 if [[ $DIFFTYPE == "U" ]]; then 1004 print "$UDIFFCSS" 1005 fi 1006 1007 cat <<-EOF 1008 </head> 1009 <body id="SUNWwebrev"> 1010 <h2>$TPATH</h2> 1011 <a class="print" href="javascript:print()">Print this page</a> 1012 <pre>$COMMENT</pre> 1013 <pre> 1014 EOF 1015 1016 html_quote | $AWK ' 1017 /^--- new/ { next } 1018 /^\+\+\+ new/ { next } 1019 /^--- old/ { next } 1020 /^\*\*\* old/ { next } 1021 /^\*\*\*\*/ { next } 1022 /^-------/ { printf "<center><h1>%s</h1></center>\n", $0; next } 1023 /^\@\@.*\@\@$/ { printf "</pre><hr /><pre>\n"; 1024 printf "<span class=\"newmarker\">%s</span>\n", $0; 1025 next} 1026 1027 /^\*\*\*/ { printf "</pre><hr /><pre><span class=\"oldmarker\">%s</span>\n", $0; 1028 next} 1029 /^---/ { printf "<span class=\"newmarker\">%s</span>\n", $0; 1030 next} 1031 /^\+/ {printf "<span class=\"new\">%s</span>\n", $0; next} 1032 /^!/ {printf "<span class=\"changed\">%s</span>\n", $0; next} 1033 /^-/ {printf "<span class=\"removed\">%s</span>\n", $0; next} 1034 {printf "%s\n", $0; next} 1035 ' 1036 1037 print "</pre></body></html>\n" 1038 } 1039 1040 1041 # 1042 # source_to_html { new | old } <filename> 1043 # 1044 # Process a plain vanilla source file to transform it into an HTML file. 1045 # 1046 source_to_html() 1047 { 1048 WHICH=$1 1049 TNAME=$2 1050 1051 print "$HTML<head>$STDHEAD" 1052 print "<title>$WHICH $TNAME</title>" 1053 print "</head><body id=\"SUNWwebrev\">" 1054 print "<pre>" 1055 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }' 1056 print "</pre></body></html>" 1057 } 1058 1059 # 1060 # teamwarecomments {text|html} parent-file child-file 1061 # 1062 # Find the first delta in the child that's not in the parent. Get the 1063 # newest delta from the parent, get all deltas from the child starting 1064 # with that delta, and then get all info starting with the second oldest 1065 # delta in that list (the first delta unique to the child). 1066 # 1067 # This code adapted from Bill Shannon's "spc" script 1068 # 1069 comments_from_teamware() 1070 { 1071 fmt=$1 1072 pfile=$PWS/$2 1073 cfile=$CWS/$3 1074 1075 psid=$($SCCS prs -d:I: $pfile 2>/dev/null) 1076 if [[ -z "$psid" ]]; then 1077 psid=1.1 1078 fi 1079 1080 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null) 1081 N=${#sids[@]} 1082 1083 nawkprg=' 1084 /^COMMENTS:/ {p=1; next} 1085 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; } 1086 NF == 0u { next } 1087 {if (p==0) next; print $0 }' 1088 1089 if [[ $N -ge 2 ]]; then 1090 sid1=${sids[$((N-2))]} # Gets 2nd to last sid 1091 1092 if [[ $fmt == "text" ]]; then 1093 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1094 $AWK "$nawkprg" 1095 return 1096 fi 1097 1098 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1099 html_quote | bug2url | sac2url | $AWK "$nawkprg" 1100 fi 1101 } 1102 1103 # 1104 # wxcomments {text|html} filepath 1105 # 1106 # Given the pathname of a file, find its location in a "wx" active file 1107 # list and print the following sccs comment. Output is either text or 1108 # HTML; if the latter, embedded bugids (sequence of 5 or more digits) are 1109 # turned into URLs. 1110 # 1111 comments_from_wx() 1112 { 1113 typeset fmt=$1 1114 typeset p=$2 1115 1116 comm=`$AWK ' 1117 $1 == "'$p'" { 1118 do getline ; while (NF > 0) 1119 getline 1120 while (NF > 0) { print ; getline } 1121 exit 1122 }' < $wxfile` 1123 1124 if [[ $fmt == "text" ]]; then 1125 print "$comm" 1126 return 1127 fi 1128 1129 print "$comm" | html_quote | bug2url | sac2url 1130 } 1131 1132 comments_from_mercurial() 1133 { 1134 fmt=$1 1135 pfile=$PWS/$2 1136 cfile=$CWS/$3 1137 1138 logdir=`dirname $cfile` 1139 logf=`basename $cfile` 1140 if [ -d $logdir ]; then 1141 ( cd $logdir; 1142 active=`hg status $logf 2>/dev/null` 1143 # If the output from 'hg status' is not empty, it means the file 1144 # hasn't been committed, so don't fetch comments. 1145 if [[ -z $active ]] ; then 1146 if [[ -n $ALL_CREV ]]; then 1147 rev_opt= 1148 for rev in $ALL_CREV; do 1149 rev_opt="$rev_opt --rev $rev" 1150 done 1151 comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf` 1152 elif [[ -n $FIRST_CREV ]]; then 1153 comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf` 1154 else 1155 comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf` 1156 fi 1157 else 1158 comm="" 1159 fi 1160 if [[ $fmt == "text" ]]; then 1161 print "$comm" 1162 return 1163 fi 1164 1165 print "$comm" | html_quote | bug2url | sac2url 1166 ) 1167 fi 1168 } 1169 1170 1171 # 1172 # getcomments {text|html} filepath parentpath 1173 # 1174 # Fetch the comments depending on what SCM mode we're in. 1175 # 1176 getcomments() 1177 { 1178 typeset fmt=$1 1179 typeset p=$2 1180 typeset pp=$3 1181 1182 if [[ -n $wxfile ]]; then 1183 comments_from_wx $fmt $p 1184 else 1185 if [[ $SCM_MODE == "teamware" ]]; then 1186 comments_from_teamware $fmt $pp $p 1187 elif [[ $SCM_MODE == "mercurial" ]]; then 1188 comments_from_mercurial $fmt $pp $p 1189 fi 1190 fi 1191 } 1192 1193 # 1194 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged> 1195 # 1196 # Print out Code Inspection figures similar to sccs-prt(1) format. 1197 # 1198 function printCI 1199 { 1200 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5 1201 typeset str 1202 if (( tot == 1 )); then 1203 str="line" 1204 else 1205 str="lines" 1206 fi 1207 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \ 1208 $tot $str $ins $del $mod $unc 1209 } 1210 1211 1212 # 1213 # difflines <oldfile> <newfile> 1214 # 1215 # Calculate and emit number of added, removed, modified and unchanged lines, 1216 # and total lines changed, the sum of added + removed + modified. 1217 # 1218 function difflines 1219 { 1220 integer tot mod del ins unc err 1221 typeset filename 1222 1223 eval $( diff -e $1 $2 | $AWK ' 1224 # Change range of lines: N,Nc 1225 /^[0-9]*,[0-9]*c$/ { 1226 n=split(substr($1,1,length($1)-1), counts, ","); 1227 if (n != 2) { 1228 error=2 1229 exit; 1230 } 1231 # 1232 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines. 1233 # following would be 5 - 3 = 2! Hence +1 for correction. 1234 # 1235 r=(counts[2]-counts[1])+1; 1236 1237 # 1238 # Now count replacement lines: each represents a change instead 1239 # of a delete, so increment c and decrement r. 1240 # 1241 while (getline != /^\.$/) { 1242 c++; 1243 r--; 1244 } 1245 # 1246 # If there were more replacement lines than original lines, 1247 # then r will be negative; in this case there are no deletions, 1248 # but there are r changes that should be counted as adds, and 1249 # since r is negative, subtract it from a and add it to c. 1250 # 1251 if (r < 0) { 1252 a-=r; 1253 c+=r; 1254 } 1255 1256 # 1257 # If there were more original lines than replacement lines, then 1258 # r will be positive; in this case, increment d by that much. 1259 # 1260 if (r > 0) { 1261 d+=r; 1262 } 1263 next; 1264 } 1265 1266 # Change lines: Nc 1267 /^[0-9].*c$/ { 1268 # The first line is a replacement; any more are additions. 1269 if (getline != /^\.$/) { 1270 c++; 1271 while (getline != /^\.$/) a++; 1272 } 1273 next; 1274 } 1275 1276 # Add lines: both Na and N,Na 1277 /^[0-9].*a$/ { 1278 while (getline != /^\.$/) a++; 1279 next; 1280 } 1281 1282 # Delete range of lines: N,Nd 1283 /^[0-9]*,[0-9]*d$/ { 1284 n=split(substr($1,1,length($1)-1), counts, ","); 1285 if (n != 2) { 1286 error=2 1287 exit; 1288 } 1289 # 1290 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines. 1291 # following would be 5 - 3 = 2! Hence +1 for correction. 1292 # 1293 r=(counts[2]-counts[1])+1; 1294 d+=r; 1295 next; 1296 } 1297 1298 # Delete line: Nd. For example 10d says line 10 is deleted. 1299 /^[0-9]*d$/ {d++; next} 1300 1301 # Should not get here! 1302 { 1303 error=1; 1304 exit; 1305 } 1306 1307 # Finish off - print results 1308 END { 1309 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n", 1310 (c+d+a), c, d, a, error); 1311 }' ) 1312 1313 # End of $AWK, Check to see if any trouble occurred. 1314 if (( $? > 0 || err > 0 )); then 1315 print "Unexpected Error occurred reading" \ 1316 "\`diff -e $1 $2\`: \$?=$?, err=" $err 1317 return 1318 fi 1319 1320 # Accumulate totals 1321 (( TOTL += tot )) 1322 (( TMOD += mod )) 1323 (( TDEL += del )) 1324 (( TINS += ins )) 1325 # Calculate unchanged lines 1326 unc=`wc -l < $1` 1327 if (( unc > 0 )); then 1328 (( unc -= del + mod )) 1329 (( TUNC += unc )) 1330 fi 1331 # print summary 1332 print "<span class=\"lineschanged\">\c" 1333 printCI $tot $ins $del $mod $unc 1334 print "</span>" 1335 } 1336 1337 1338 # 1339 # flist_from_wx 1340 # 1341 # Sets up webrev to source its information from a wx-formatted file. 1342 # Sets the global 'wxfile' variable. 1343 # 1344 function flist_from_wx 1345 { 1346 typeset argfile=$1 1347 if [[ -n ${argfile%%/*} ]]; then 1348 # 1349 # If the wx file pathname is relative then make it absolute 1350 # because the webrev does a "cd" later on. 1351 # 1352 wxfile=$PWD/$argfile 1353 else 1354 wxfile=$argfile 1355 fi 1356 1357 $AWK '{ c = 1; print; 1358 while (getline) { 1359 if (NF == 0) { c = -c; continue } 1360 if (c > 0) print 1361 } 1362 }' $wxfile > $FLIST 1363 1364 print " Done." 1365 } 1366 1367 # 1368 # flist_from_teamware [ <args-to-putback-n> ] 1369 # 1370 # Generate the file list by extracting file names from a putback -n. Some 1371 # names may come from the "update/create" messages and others from the 1372 # "currently checked out" warning. Renames are detected here too. Extract 1373 # values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback 1374 # -n as well, but remove them if they are already defined. 1375 # 1376 function flist_from_teamware 1377 { 1378 if [[ -n $codemgr_parent ]]; then 1379 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then 1380 print -u2 "parent $codemgr_parent doesn't look like a" \ 1381 "valid teamware workspace" 1382 exit 1 1383 fi 1384 parent_args="-p $codemgr_parent" 1385 fi 1386 1387 print " File list from: 'putback -n $parent_args $*' ... \c" 1388 1389 putback -n $parent_args $* 2>&1 | 1390 $AWK ' 1391 /^update:|^create:/ {print $2} 1392 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)} 1393 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)} 1394 /^The following files are currently checked out/ {p = 1; next} 1395 NF == 0 {p=0 ; next} 1396 /^rename/ {old=$3} 1397 $1 == "to:" {print $2, old} 1398 /^"/ {next} 1399 p == 1 {print $1}' | 1400 sort -r -k 1,1 -u | sort > $FLIST 1401 1402 print " Done." 1403 } 1404 1405 function outgoing_from_mercurial_forest 1406 { 1407 hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK ' 1408 BEGIN {ntree=0} 1409 /^comparing/ {next} 1410 /^no changes/ {next} 1411 /^searching/ {next} 1412 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); 1413 trees[ntree++] = tree; 1414 revs[tree]=-1; 1415 next} 1416 /^rev:/ {rev=$2+0; 1417 if (revs[tree] == -1 || rev < revs[tree]) 1418 { revs[tree] = rev; }; 1419 next;} 1420 END {for (tree in trees) 1421 { rev=revs[trees[tree]]; 1422 if (rev > 0) 1423 {printf("%s %d\n",trees[tree],rev-1)} 1424 }}' | while read LINE 1425 do 1426 set - $LINE 1427 TREE=$1 1428 REV=$2 1429 A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'` 1430 FSTAT_OPT="--rev $A" 1431 print "Revision: $A $REV" >> $FLIST 1432 treestatus $TREE 1433 done 1434 } 1435 1436 function flist_from_mercurial_forest 1437 { 1438 rm -f $FLIST 1439 if [ -z "$Nflag" ]; then 1440 print " File list from hg foutgoing $PWS ..." 1441 outgoing_from_mercurial_forest 1442 HG_LIST_FROM_COMMIT=1 1443 fi 1444 if [ ! -f $FLIST ]; then 1445 # hg commit hasn't been run see what is lying around 1446 print "\n No outgoing, perhaps you haven't commited." 1447 print " File list from hg fstatus -mard ...\c" 1448 FSTAT_OPT= 1449 fstatus 1450 HG_LIST_FROM_COMMIT=0 1451 fi 1452 print " Done." 1453 } 1454 1455 # 1456 # Used when dealing with the result of 'hg foutgoing' 1457 # When now go down the tree and generate the change list 1458 # 1459 function treestatus 1460 { 1461 TREE=$1 1462 HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT" 1463 1464 $HGCMD -mdn 2>/dev/null | $FILTER | while read F 1465 do 1466 echo $TREE/$F 1467 done >> $FLIST 1468 1469 # Then all the added files 1470 # But some of these could have been "moved" or renamed ones 1471 # so let's make sure we get the proper info 1472 # hg status -aC will produce something like: 1473 # A subdir/File3 1474 # A subdir/File4 1475 # File4 1476 # A subdir/File5 1477 # The first and last are simple addition while the middle one 1478 # is a move/rename 1479 1480 $HGCMD -aC | $FILTER | while read LINE; do 1481 ldone="" 1482 while [ -z "$ldone" ]; do 1483 ldone="1" 1484 set - $LINE 1485 if [ $# -eq 2 -a "$1" == "A" ]; then 1486 AFILE=$2 1487 if read LINE2; then 1488 set - $LINE2 1489 if [ $# -eq 1 ]; then 1490 echo $TREE/$AFILE $TREE/$1 >>$FLIST 1491 elif [ $# -eq 2 ]; then 1492 echo $TREE/$AFILE >>$FLIST 1493 LINE=$LINE2 1494 ldone="" 1495 fi 1496 else 1497 echo $TREE/$AFILE >>$FLIST 1498 fi 1499 fi 1500 done 1501 done 1502 $HGCMD -rn | $FILTER | while read RFILE; do 1503 grep "$TREE/$RFILE" $FLIST >/dev/null 1504 if [ $? -eq 1 ]; then 1505 echo $TREE/$RFILE >>$FLIST 1506 fi 1507 done 1508 } 1509 1510 function fstatus 1511 { 1512 # 1513 # forest extension is still being changed. For instance the output 1514 # of fstatus used to no prepend the tree path to filenames, but 1515 # this has changed recently. AWK code below does try to handle both 1516 # cases 1517 # 1518 hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK ' 1519 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next} 1520 $1 != "" {n=index($1,tree); 1521 if (n == 0) 1522 { printf("%s/%s\n",tree,$1)} 1523 else 1524 { printf("%s\n",$1)}}' >> $FLIST 1525 1526 # 1527 # There is a bug in the output of fstatus -aC on recent versions: it 1528 # inserts a space between the name of the tree and the filename of the 1529 # old file. e.g.: 1530 # 1531 # $ hg fstatus -aC 1532 # [.] 1533 # 1534 # [MyWS] 1535 # A MyWS/subdir/File2 1536 # MyWS/ File2 1537 # 1538 # [MyWS2] 1539 # 1540 1541 hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK ' 1542 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next} 1543 /^A .*/ {n=index($2,tree); 1544 if (n == 0) 1545 { printf("A %s/%s\n",tree,$2)} 1546 else 1547 { printf("A %s\n",$2)}; 1548 next} 1549 /^ / {n=index($1,tree); 1550 if (n == 0) 1551 { printf("%s/%s\n",tree,$1)} 1552 else 1553 { if (NF == 2) 1554 printf("%s/%s\n",tree,$2) 1555 else 1556 printf("%s\n",$1) 1557 }; 1558 next} 1559 ' | while read LINE; do 1560 ldone="" 1561 while [ -z "$ldone" ]; do 1562 ldone="1" 1563 set - $LINE 1564 if [ $# -eq 2 -a "$1" == "A" ]; then 1565 AFILE=$2 1566 if read LINE2; then 1567 set - $LINE2 1568 if [ $# -eq 1 ]; then 1569 echo $AFILE $1 >>$FLIST 1570 elif [ $# -eq 2 ]; then 1571 echo $AFILE >>$FLIST 1572 LINE=$LINE2 1573 ldone="" 1574 fi 1575 else 1576 echo $AFILE >>$FLIST 1577 fi 1578 fi 1579 done 1580 done 1581 hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK ' 1582 /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next} 1583 $1 != "" {n=index($1,tree); 1584 if (n == 0) 1585 { printf("%s/%s\n",tree,$1)} 1586 else 1587 { printf("%s\n",$1)}}' | while read RFILE; do 1588 grep "$RFILE" $FLIST >/dev/null 1589 if [ $? -eq 1 ]; then 1590 echo $RFILE >>$FLIST 1591 fi 1592 done 1593 } 1594 1595 # 1596 # flist_from_mercurial $PWS 1597 # 1598 # Only local file based repositories are supported at present 1599 # since even though we can determine the list from the parent finding 1600 # the changes is harder. 1601 # 1602 # We first look for any outgoing files, this is for when the user has 1603 # run hg commit. If we don't find any then we look with hg status. 1604 # 1605 # We need at least one of default-push or default paths set in .hg/hgrc 1606 # If neither are set we don't know who to compare with. 1607 1608 function flist_from_mercurial 1609 { 1610 # if [ "${PWS##ssh://}" != "$PWS" -o \ 1611 # "${PWS##http://}" != "$PWS" -o \ 1612 # "${PWS##https://}" != "$PWS" ]; then 1613 # print "Remote Mercurial repositories not currently supported." 1614 # print "Set default and/or default-push to a local repository" 1615 # exit 1616 # fi 1617 if [[ -n $forestflag ]]; then 1618 HG_LIST_FROM_COMMIT= 1619 flist_from_mercurial_forest 1620 else 1621 STATUS_REV= 1622 if [[ -n $rflag ]]; then 1623 STATUS_REV="--rev $PARENT_REV" 1624 elif [[ -n $OUTREV ]]; then 1625 STATUS_REV="--rev $OUTREV" 1626 else 1627 # hg commit hasn't been run see what is lying around 1628 print "\n No outgoing, perhaps you haven't commited." 1629 fi 1630 # First let's list all the modified or deleted files 1631 1632 hg status $STATUS_REV -mdn | $FILTER > $FLIST 1633 1634 # Then all the added files 1635 # But some of these could have been "moved" or renamed ones 1636 # so let's make sure we get the proper info 1637 # hg status -aC will produce something like: 1638 # A subdir/File3 1639 # A subdir/File4 1640 # File4 1641 # A subdir/File5 1642 # The first and last are simple addition while the middle one 1643 # is a move/rename 1644 1645 hg status $STATUS_REV -aC | $FILTER >$FLIST.temp 1646 while read LINE; do 1647 ldone="" 1648 while [ -z "$ldone" ]; do 1649 ldone="1" 1650 set - $LINE 1651 if [ $# -eq 2 -a "$1" == "A" ]; then 1652 AFILE=$2 1653 if read LINE2; then 1654 set - $LINE2 1655 if [ $# -eq 1 ]; then 1656 echo $AFILE $1 >>$FLIST 1657 elif [ $# -eq 2 ]; then 1658 echo $AFILE >>$FLIST 1659 LINE=$LINE2 1660 ldone="" 1661 fi 1662 else 1663 echo $AFILE >>$FLIST 1664 fi 1665 fi 1666 done 1667 done < $FLIST.temp 1668 hg status $STATUS_REV -rn | $FILTER > $FLIST.temp 1669 while read RFILE; do 1670 grep "$RFILE" $FLIST >/dev/null 1671 if [ $? -eq 1 ]; then 1672 echo $RFILE >>$FLIST 1673 fi 1674 done < $FLIST.temp 1675 rm -f $FLIST.temp 1676 fi 1677 } 1678 1679 function env_from_flist 1680 { 1681 [[ -r $FLIST ]] || return 1682 1683 # 1684 # Use "eval" to set env variables that are listed in the file 1685 # list. Then copy those into our local versions of those 1686 # variables if they have not been set already. 1687 # 1688 eval `sed -e "s/#.*$//" $FLIST | grep = ` 1689 1690 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1691 1692 # 1693 # Check to see if CODEMGR_PARENT is set in the flist file. 1694 # 1695 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 1696 codemgr_parent=$CODEMGR_PARENT 1697 } 1698 1699 # 1700 # detect_scm 1701 # 1702 # We dynamically test the SCM type; this allows future extensions to 1703 # new SCM types 1704 # 1705 function detect_scm 1706 { 1707 # 1708 # If CODEMGR_WS is specified in the flist file, we assume teamware. 1709 # 1710 if [[ -r $FLIST ]]; then 1711 egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1 1712 if [[ $? -eq 0 ]]; then 1713 print "teamware" 1714 return 1715 fi 1716 fi 1717 1718 # 1719 # The presence of $CODEMGR_WS and a Codemgr_wsdata directory 1720 # is our clue that this is a teamware workspace. 1721 # Same if true if current directory has a Codemgr_wsdata sub-dir 1722 # 1723 if [[ -z "$CODEMGR_WS" ]]; then 1724 CODEMGR_WS=`workspace name 2>/dev/null` 1725 fi 1726 1727 if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then 1728 print "teamware" 1729 elif [[ -d $PWD/Codemgr_wsdata ]]; then 1730 print "teamware" 1731 elif hg root >/dev/null ; then 1732 print "mercurial" 1733 else 1734 print "unknown" 1735 fi 1736 } 1737 1738 # 1739 # Extract the parent workspace from the Codemgr_wsdata/parent file 1740 # 1741 function parent_from_teamware 1742 { 1743 if [[ -f "$1/Codemgr_wsdata/parent" ]]; then 1744 tail -1 "$1/Codemgr_wsdata/parent" 1745 fi 1746 } 1747 1748 function look_for_prog 1749 { 1750 typeset path 1751 typeset ppath 1752 typeset progname=$1 1753 1754 DEVTOOLS= 1755 OS=`uname` 1756 if [[ "$OS" == "SunOS" ]]; then 1757 DEVTOOLS="/java/devtools/`uname -p`/bin" 1758 elif [[ "$OS" == "Linux" ]]; then 1759 DEVTOOLS="/java/devtools/linux/bin" 1760 fi 1761 1762 ppath=$PATH 1763 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin 1764 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin 1765 ppath=$ppath:/opt/onbld/bin/`uname -p` 1766 ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS 1767 1768 PATH=$ppath prog=`whence $progname` 1769 if [[ -n $prog ]]; then 1770 print $prog 1771 fi 1772 } 1773 1774 function build_old_new_teamware 1775 { 1776 # If the child's version doesn't exist then 1777 # get a readonly copy. 1778 1779 if [[ ! -f $F && -f SCCS/s.$F ]]; then 1780 $SCCS get -s $F 1781 fi 1782 1783 # 1784 # Snag new version of file. 1785 # 1786 rm -f $newdir/$DIR/$F 1787 cp $F $newdir/$DIR/$F 1788 1789 # 1790 # Get the parent's version of the file. First see whether the 1791 # child's version is checked out and get the parent's version 1792 # with keywords expanded or unexpanded as appropriate. 1793 # 1794 if [ -f $PWS/$PDIR/SCCS/s.$PF -o \ 1795 -f $PWS/$PDIR/SCCS/p.$PF ]; then 1796 rm -f $olddir/$PDIR/$PF 1797 if [ -f SCCS/p.$F ]; then 1798 $SCCS get -s -p -k $PWS/$PDIR/$PF \ 1799 > $olddir/$PDIR/$PF 1800 else 1801 $SCCS get -s -p $PWS/$PDIR/$PF \ 1802 > $olddir/$PDIR/$PF 1803 fi 1804 else 1805 if [[ -f $PWS/$PDIR/$PF ]]; then 1806 # Parent is not a real workspace, but just a raw 1807 # directory tree - use the file that's there as 1808 # the old file. 1809 1810 rm -f $olddir/$DIR/$F 1811 cp $PWS/$PDIR/$PF $olddir/$DIR/$F 1812 fi 1813 fi 1814 } 1815 1816 # 1817 # Find the parent for $1 1818 # 1819 function find_outrev 1820 { 1821 crev=$1 1822 prev=`hg log -r $crev --template '{parents}\n'` 1823 if [[ -z "$prev" ]] 1824 then 1825 # No specific parent means previous changeset is parent 1826 prev=`expr $crev - 1` 1827 else 1828 # Format is either of the following two: 1829 # 546:7df6fcf1183b 1830 # 548:16f1915bb5cd 547:ffaa4e775815 1831 prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'` 1832 fi 1833 print $prev 1834 } 1835 1836 function extract_ssh_infos 1837 { 1838 CMD=$1 1839 if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then 1840 ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'` 1841 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'` 1842 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'` 1843 else 1844 ssh_user= 1845 ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'` 1846 ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'` 1847 fi 1848 1849 } 1850 1851 function build_old_new_mercurial 1852 { 1853 olddir=$1 1854 newdir=$2 1855 DIR=$3 1856 F=$4 1857 # 1858 # new version of the file. 1859 # 1860 rm -rf $newdir/$DIR/$F 1861 if [ -f $F ]; then 1862 cp $F $newdir/$DIR/$F 1863 fi 1864 1865 # 1866 # Old version of the file. 1867 # 1868 rm -rf $olddir/$DIR/$F 1869 1870 if [ -n "$PWS" ]; then 1871 if expr "$PWS" : 'ssh://' >/dev/null 1872 then 1873 extract_ssh_infos $PWS 1874 if [ -n "$ssh_user" ]; then 1875 parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir" 1876 else 1877 parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir" 1878 fi 1879 else 1880 parent="hg -R $PWS --cwd $PWS" 1881 fi 1882 else 1883 parent="" 1884 fi 1885 1886 if [ -z "$rename" ]; then 1887 if [ -n "$rflag" ]; then 1888 parentrev=$PARENT_REV 1889 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then 1890 parentrev=$OUTREV 1891 else 1892 if [[ -n $HG_BRANCH ]]; then 1893 parentrev=$HG_BRANCH 1894 else 1895 parentrev="tip" 1896 fi 1897 fi 1898 1899 if [ -n "$parentrev" ]; then 1900 if [ -z "$parent" ]; then 1901 hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null 1902 else 1903 # when specifying a workspace we have to provide 1904 # the full path 1905 $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null 1906 fi 1907 fi 1908 else 1909 # It's a rename (or a move), so let's make sure we move 1910 # to the right directory first, then restore it once done 1911 current_dir=`pwd` 1912 cd $CWS/$PDIR 1913 if [ -n "$rflag" ]; then 1914 parentrev=$PARENT_REV 1915 elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then 1916 parentrev=$OUTREV 1917 fi 1918 if [ -z "$parentrev" ]; then 1919 parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'` 1920 fi 1921 if [ -n "$parentrev" ]; then 1922 mkdir -p $olddir/$PDIR 1923 if [ -z "$parent" ]; then 1924 hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null 1925 else 1926 $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null 1927 fi 1928 fi 1929 cd $current_dir 1930 fi 1931 } 1932 1933 function build_old_new 1934 { 1935 if [[ $SCM_MODE == "teamware" ]]; then 1936 build_old_new_teamware $@ 1937 fi 1938 1939 if [[ $SCM_MODE == "mercurial" ]]; then 1940 build_old_new_mercurial $@ 1941 fi 1942 } 1943 1944 1945 # 1946 # Usage message. 1947 # 1948 function usage 1949 { 1950 print "Usage:\twebrev [common-options] 1951 webrev [common-options] ( <file> | - ) 1952 webrev [common-options] -w <wx file> 1953 webrev [common-options] -l [arguments to 'putback'] 1954 1955 Options: 1956 -v: Print the version of this tool. 1957 -b: Do not ignore changes in the amount of white space. 1958 -c <CR#>: Include link to CR (aka bugid) in the main page. 1959 -O: Print bugids/arc cases suitable for OpenJDK. 1960 -i <filename>: Include <filename> in the index.html file. 1961 -o <outdir>: Output webrev to specified directory. 1962 -p <compare-against>: Use specified parent wkspc or basis for comparison 1963 -w <wxfile>: Use specified wx active file. 1964 -u <username>: Use that username instead of 'guessing' one. 1965 -m: Forces the use of Mercurial 1966 -t: Forces the use of Teamware 1967 1968 Mercurial only options: 1969 -r rev: Compare against a specified revision 1970 -N: Skip 'hg outgoing', use only 'hg status' 1971 -f: Use the forest extension 1972 1973 Environment: 1974 WDIR: Control the output directory. 1975 WEBREV_BUGURL: Control the URL prefix for bugids. 1976 WEBREV_SACURL: Control the URL prefix for ARC cases. 1977 1978 SCM Environment: 1979 Teamware: CODEMGR_WS: Workspace location. 1980 Teamware: CODEMGR_PARENT: Parent workspace location. 1981 1982 " 1983 1984 exit 2 1985 } 1986 1987 # 1988 # 1989 # Main program starts here 1990 # 1991 # 1992 LANG="C" 1993 LC_ALL="C" 1994 export LANG LC_ALL 1995 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15 1996 1997 set +o noclobber 1998 1999 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` 2000 [[ -z $WX ]] && WX=`look_for_prog wx` 2001 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` 2002 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf` 2003 [[ -z $PERL ]] && PERL=`look_for_prog perl` 2004 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs` 2005 [[ -z $AWK ]] && AWK=`look_for_prog nawk` 2006 [[ -z $AWK ]] && AWK=`look_for_prog gawk` 2007 [[ -z $AWK ]] && AWK=`look_for_prog awk` 2008 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace` 2009 [[ -z $JAR ]] && JAR=`look_for_prog jar` 2010 [[ -z $ZIP ]] && ZIP=`look_for_prog zip` 2011 [[ -z $GETENT ]] && GETENT=`look_for_prog getent` 2012 [[ -z $WGET ]] && WGET=`look_for_prog wget` 2013 2014 if uname | grep CYGWIN >/dev/null 2015 then 2016 ISWIN=1 2017 # Under windows mercurial outputs '\' instead of '/' 2018 FILTER="tr '\\\\' '/'" 2019 else 2020 FILTER="cat" 2021 fi 2022 2023 if [[ ! -x $PERL ]]; then 2024 print -u2 "Error: No perl interpreter found. Exiting." 2025 exit 1 2026 fi 2027 2028 # 2029 # These aren't fatal, but we want to note them to the user. 2030 # We don't warn on the absence of 'wx' until later when we've 2031 # determined that we actually need to try to invoke it. 2032 # 2033 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found." 2034 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found." 2035 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found." 2036 2037 # Declare global total counters. 2038 integer TOTL TINS TDEL TMOD TUNC 2039 2040 flist_mode= 2041 flist_file= 2042 bflag= 2043 iflag= 2044 oflag= 2045 pflag= 2046 uflag= 2047 lflag= 2048 wflag= 2049 Oflag= 2050 rflag= 2051 Nflag= 2052 forestflag= 2053 while getopts "c:i:o:p:r:u:lmtwONvfb" opt 2054 do 2055 case $opt in 2056 b) bflag=1;; 2057 2058 i) iflag=1 2059 INCLUDE_FILE=$OPTARG;; 2060 2061 o) oflag=1 2062 WDIR=$OPTARG;; 2063 2064 p) pflag=1 2065 codemgr_parent=$OPTARG;; 2066 2067 u) uflag=1 2068 username=$OPTARG;; 2069 2070 c) if [[ -z $CRID ]]; then 2071 CRID=$OPTARG 2072 else 2073 CRID="$CRID $OPTARG" 2074 fi;; 2075 2076 m) SCM_MODE="mercurial";; 2077 2078 t) SCM_MODE="teamware";; 2079 2080 # 2081 # If -l has been specified, we need to abort further options 2082 # processing, because subsequent arguments are going to be 2083 # arguments to 'putback -n'. 2084 # 2085 l) lflag=1 2086 break;; 2087 2088 w) wflag=1;; 2089 2090 O) Oflag=1;; 2091 2092 N) Nflag=1;; 2093 2094 f) forestflag=1;; 2095 2096 r) rflag=1 2097 PARENT_REV=$OPTARG;; 2098 2099 v) print "$0 version: $WEBREV_UPDATED";; 2100 2101 2102 ?) usage;; 2103 esac 2104 done 2105 2106 FLIST=/tmp/$$.flist 2107 2108 if [[ -n $wflag && -n $lflag ]]; then 2109 usage 2110 fi 2111 2112 if [[ -n $forestflag && -n $rflag ]]; then 2113 print "The -r <rev> flag is incompatible with the use of forests" 2114 exit 2 2115 fi 2116 2117 # 2118 # If this manually set as the parent, and it appears to be an earlier webrev, 2119 # then note that fact and set the parent to the raw_files/new subdirectory. 2120 # 2121 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then 2122 parent_webrev="$codemgr_parent" 2123 codemgr_parent="$codemgr_parent/raw_files/new" 2124 fi 2125 2126 if [[ -z $wflag && -z $lflag ]]; then 2127 shift $(($OPTIND - 1)) 2128 2129 if [[ $1 == "-" ]]; then 2130 cat > $FLIST 2131 flist_mode="stdin" 2132 flist_done=1 2133 shift 2134 elif [[ -n $1 ]]; then 2135 if [[ ! -r $1 ]]; then 2136 print -u2 "$1: no such file or not readable" 2137 usage 2138 fi 2139 cat $1 > $FLIST 2140 flist_mode="file" 2141 flist_file=$1 2142 flist_done=1 2143 shift 2144 else 2145 flist_mode="auto" 2146 fi 2147 fi 2148 2149 # 2150 # Before we go on to further consider -l and -w, work out which SCM we think 2151 # is in use. 2152 # 2153 if [[ -z $SCM_MODE ]]; then 2154 SCM_MODE=`detect_scm $FLIST` 2155 fi 2156 if [[ $SCM_MODE == "unknown" ]]; then 2157 print -u2 "Unable to determine SCM type currently in use." 2158 print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in" 2159 print -u2 " the environment or in the file list." 2160 print -u2 "For mercurial: webrev runs 'hg root'." 2161 exit 1 2162 fi 2163 2164 print -u2 " SCM detected: $SCM_MODE" 2165 2166 2167 if [[ $SCM_MODE == "mercurial" ]]; then 2168 # 2169 # determine Workspace and parent workspace paths 2170 # 2171 CWS=`hg root | $FILTER` 2172 if [[ -n $pflag && -z "$PWS" ]]; then 2173 OUTPWS=$codemgr_parent 2174 # Let's try to expand it if it's an alias defined in [paths] 2175 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER` 2176 if [[ -n $tmp ]]; then 2177 OUTPWS="$tmp" 2178 fi 2179 if [[ -n $rflag ]]; then 2180 if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then 2181 PWS=$codemgr_parent 2182 else 2183 PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER` 2184 fi 2185 fi 2186 fi 2187 # 2188 # OUTPWS is the parent repository to use when using 'hg outgoing' 2189 # 2190 if [[ -z $Nflag ]]; then 2191 if [[ -n $forestflag ]]; then 2192 # 2193 # for forest we have to rely on properly set default and 2194 # default-push because they can be different from the top one. 2195 # unless of course it was explicitely speficied with -p 2196 if [[ -z $pflag ]]; then 2197 OUTPWS= 2198 fi 2199 else 2200 # 2201 # Unfortunately mercurial is bugged and doesn't handle 2202 # aliases correctly in 'hg path default' 2203 # So let's do it ourselves. Sigh... 2204 if [[ -z "$OUTPWS" ]]; then 2205 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER` 2206 fi 2207 # Still empty, means no default-push 2208 if [[ -z "$OUTPWS" ]]; then 2209 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER` 2210 fi 2211 # Let's try to expand it if it's an alias defined in [paths] 2212 tmp=`hg path $OUTPWS 2>/dev/null | $FILTER` 2213 if [[ -n $tmp ]]; then 2214 OUTPWS="$tmp" 2215 fi 2216 fi 2217 fi 2218 # 2219 # OUTPWS may contain username:password, let's make sure we remove the 2220 # sensitive information before we print out anything in the HTML 2221 # 2222 OUTPWS2=$OUTPWS 2223 if [[ -n $OUTPWS ]]; then 2224 if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then 2225 # Remove everything between '://' and '@' 2226 OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'` 2227 fi 2228 fi 2229 2230 if [[ -z $HG_BRANCH ]]; then 2231 HG_BRANCH=`hg branch` 2232 if [ "$HG_BRANCH" == "default" ]; then 2233 # 2234 # 'default' means no particular branch, so let's cancel that 2235 # 2236 HG_BRANCH= 2237 fi 2238 fi 2239 2240 if [[ -z $forestflag ]]; then 2241 if [[ -z $Nflag ]]; then 2242 # 2243 # If no "-N", always do "hg outgoing" against parent 2244 # repository to determine list of outgoing revisions. 2245 # 2246 ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n` 2247 if [[ -n $ALL_CREV ]]; then 2248 FIRST_CREV=`echo "$ALL_CREV" | head -1` 2249 # 2250 # If no "-r", choose revision to compare against by 2251 # finding the latest revision not in the outgoing list. 2252 # 2253 if [[ -z $rflag ]]; then 2254 OUTREV=`find_outrev "$FIRST_CREV"` 2255 if [[ -n $OUTREV ]]; then 2256 HG_LIST_FROM_COMMIT=1 2257 fi 2258 fi 2259 fi 2260 elif [[ -n $rflag ]]; then 2261 # 2262 # If skipping "hg outgoing" but still comparing against a 2263 # specific revision (not the tip), set revision for comment 2264 # accumulation. 2265 # 2266 FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'` 2267 FIRST_CREV=`expr $FIRST_CREV + 1` 2268 fi 2269 fi 2270 #Let's check if a merge is needed, if so, issue a warning 2271 PREV=`hg parent | grep '^tag:.*tip$'` 2272 if [[ -z $PREV ]]; then 2273 print "WARNING: parent rev is not tip. Maybe an update or merge is needed" 2274 fi 2275 fi 2276 2277 if [[ -n $lflag ]]; then 2278 # 2279 # If the -l flag is given instead of the name of a file list, 2280 # then generate the file list by extracting file names from a 2281 # putback -n. 2282 # 2283 shift $(($OPTIND - 1)) 2284 if [[ $SCM_MODE == "teamware" ]]; then 2285 flist_from_teamware "$*" 2286 elif [[ $SCM_MODE == "mercurial" ]]; then 2287 flist_from_mercurial 2288 fi 2289 flist_done=1 2290 shift $# 2291 2292 elif [[ -n $wflag ]]; then 2293 # 2294 # If the -w is given then assume the file list is in Bonwick's "wx" 2295 # command format, i.e. pathname lines alternating with SCCS comment 2296 # lines with blank lines as separators. Use the SCCS comments later 2297 # in building the index.html file. 2298 # 2299 shift $(($OPTIND - 1)) 2300 wxfile=$1 2301 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then 2302 if [[ -r $CODEMGR_WS/wx/active ]]; then 2303 wxfile=$CODEMGR_WS/wx/active 2304 fi 2305 fi 2306 2307 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \ 2308 "be auto-detected (check \$CODEMGR_WS)" && exit 1 2309 2310 print -u2 " File list from: wx 'active' file '$wxfile' ... \c" 2311 flist_from_wx $wxfile 2312 flist_done=1 2313 if [[ -n "$*" ]]; then 2314 shift 2315 fi 2316 elif [[ $flist_mode == "stdin" ]]; then 2317 print -u2 " File list from: standard input" 2318 elif [[ $flist_mode == "file" ]]; then 2319 print -u2 " File list from: $flist_file" 2320 fi 2321 2322 if [[ $# -gt 0 ]]; then 2323 print -u2 "WARNING: unused arguments: $*" 2324 fi 2325 2326 if [[ $SCM_MODE == "teamware" ]]; then 2327 # 2328 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can 2329 # be set in a number of ways, in decreasing precedence: 2330 # 2331 # 1) on the command line (only for the parent) 2332 # 2) in the user environment 2333 # 3) in the flist 2334 # 4) automatically based on the workspace (only for the parent) 2335 # 2336 2337 # 2338 # Here is case (2): the user environment 2339 # 2340 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 2341 [[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name` 2342 2343 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then 2344 print -u2 "$codemgr_ws: no such workspace" 2345 exit 1 2346 fi 2347 2348 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 2349 codemgr_parent=$CODEMGR_PARENT 2350 2351 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then 2352 print -u2 "$codemgr_parent: no such directory" 2353 exit 1 2354 fi 2355 2356 # 2357 # If we're in auto-detect mode and we haven't already gotten the file 2358 # list, then see if we can get it by probing for wx. 2359 # 2360 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then 2361 if [[ ! -x $WX ]]; then 2362 print -u2 "WARNING: wx not found!" 2363 fi 2364 2365 # 2366 # We need to use wx list -w so that we get renamed files, etc. 2367 # but only if a wx active file exists-- otherwise wx will 2368 # hang asking us to initialize our wx information. 2369 # 2370 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then 2371 print -u2 " File list from: 'wx list -w' ... \c" 2372 $WX list -w > $FLIST 2373 $WX comments > /tmp/$$.wx_comments 2374 wxfile=/tmp/$$.wx_comments 2375 print -u2 "done" 2376 flist_done=1 2377 fi 2378 fi 2379 2380 # 2381 # If by hook or by crook we've gotten a file list by now (perhaps 2382 # from the command line), eval it to extract environment variables from 2383 # it: This is step (3). 2384 # 2385 env_from_flist 2386 2387 # 2388 # Continuing step (3): If we still have no file list, we'll try to get 2389 # it from teamware. 2390 # 2391 if [[ -z $flist_done ]]; then 2392 flist_from_teamware 2393 env_from_flist 2394 fi 2395 2396 if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then 2397 codemgr_ws=$PWD 2398 fi 2399 # 2400 # Observe true directory name of CODEMGR_WS, as used later in 2401 # webrev title. 2402 # 2403 if [[ -n $codemgr_ws ]]; then 2404 codemgr_ws=$(cd $codemgr_ws;print $PWD) 2405 fi 2406 2407 if [[ -n $codemgr_parent ]]; then 2408 codemgr_parent=$(cd $codemgr_parent;print $PWD) 2409 fi 2410 2411 # 2412 # (4) If we still don't have a value for codemgr_parent, get it 2413 # from workspace. 2414 # 2415 [[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent` 2416 [[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws` 2417 2418 if [[ ! -d $codemgr_parent ]]; then 2419 print -u2 "$CODEMGR_PARENT: no such parent workspace" 2420 exit 1 2421 fi 2422 2423 # 2424 # Reset CODEMGR_WS to make sure teamware commands are happy. 2425 # 2426 CODEMGR_WS=$codemgr_ws 2427 CWS=$codemgr_ws 2428 PWS=$codemgr_parent 2429 elif [[ $SCM_MODE == "mercurial" ]]; then 2430 if [[ -z $flist_done ]]; then 2431 flist_from_mercurial $PWS 2432 fi 2433 fi 2434 2435 # 2436 # If the user didn't specify a -i option, check to see if there is a 2437 # webrev-info file in the workspace directory. 2438 # 2439 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then 2440 iflag=1 2441 INCLUDE_FILE="$CWS/webrev-info" 2442 fi 2443 2444 if [[ -n $iflag ]]; then 2445 if [[ ! -r $INCLUDE_FILE ]]; then 2446 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \ 2447 "not readable." 2448 exit 1 2449 else 2450 # 2451 # $INCLUDE_FILE may be a relative path, and the script alters 2452 # PWD, so we just stash a copy in /tmp. 2453 # 2454 cp $INCLUDE_FILE /tmp/$$.include 2455 fi 2456 fi 2457 2458 # 2459 # Output directory. 2460 # 2461 if [[ -z $WDIR ]]; then 2462 WDIR=$CWS/webrev 2463 else 2464 # If the output directory doesn't end with '/webrev' or '/webrev/' 2465 # then add '/webrev'. This is for backward compatibility 2466 if ! expr $WDIR : '.*/webrev/\?$' >/dev/null 2467 then 2468 WDIR=$WDIR/webrev 2469 fi 2470 fi 2471 # WDIR=${WDIR:-$CWS/webrev} 2472 2473 # 2474 # Name of the webrev, derived from the workspace name; in the 2475 # future this could potentially be an option. 2476 # 2477 # Let's keep what's after the last '/' 2478 WNAME=${CWS##*/} 2479 2480 # 2481 # If WDIR doesn't start with '/' or 'x:' prepend the current dir 2482 # 2483 if [ ${WDIR%%/*} ]; then 2484 if [[ -n $ISWIN ]]; then 2485 if [ ${WDIR%%[A-Za-z]:*} ]; then 2486 WDIR=$PWD/$WDIR 2487 fi 2488 else 2489 WDIR=$PWD/$WDIR 2490 fi 2491 fi 2492 2493 if [[ ! -d $WDIR ]]; then 2494 mkdir -p $WDIR 2495 [[ $? != 0 ]] && exit 1 2496 fi 2497 2498 # 2499 # Summarize what we're going to do. 2500 # 2501 print " Workspace: $CWS" 2502 if [[ -n $parent_webrev ]]; then 2503 print "Compare against: webrev at $parent_webrev" 2504 elif [[ -n $OUTPWS2 ]]; then 2505 print "Compare against: $OUTPWS2" 2506 fi 2507 if [[ -n $HG_BRANCH ]]; then 2508 print " Branch: $HG_BRANCH" 2509 fi 2510 if [[ -n $rflag ]]; then 2511 print "Compare against version: $PARENT_REV" 2512 fi 2513 [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE" 2514 print " Output to: $WDIR" 2515 2516 # 2517 # Save the file list in the webrev dir 2518 # 2519 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list 2520 2521 # 2522 # Bug IDs will be replaced by a URL. Order of precedence 2523 # is: default location, $WEBREV_BUGURL, the -O flag. 2524 # 2525 BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr=' 2526 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL" 2527 [[ -n "$Oflag" ]] && \ 2528 BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=' 2529 2530 # 2531 # Likewise, ARC cases will be replaced by a URL. Order of precedence 2532 # is: default, $WEBREV_SACURL, the -O flag. 2533 # 2534 # Note that -O also triggers different substitution behavior for 2535 # SACURL. See sac2url(). 2536 # 2537 SACURL='http://sac.eng.sun.com' 2538 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL" 2539 [[ -n $Oflag ]] && \ 2540 SACURL='http://www.opensolaris.org/os/community/arc/caselog' 2541 2542 rm -f $WDIR/$WNAME.patch 2543 rm -f $WDIR/$WNAME.ps 2544 rm -f $WDIR/$WNAME.pdf 2545 2546 touch $WDIR/$WNAME.patch 2547 2548 print " Output Files:" 2549 2550 # 2551 # Clean up the file list: Remove comments, blank lines and env variables. 2552 # 2553 sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean 2554 FLIST=/tmp/$$.flist.clean 2555 2556 # 2557 # Clean up residual raw files 2558 # 2559 if [ -d $WDIR/raw_files ]; then 2560 rm -rf $WDIR/raw_files 2>/dev/null 2561 fi 2562 2563 # 2564 # Should we ignore changes in white spaces when generating diffs? 2565 # 2566 if [[ -n $bflag ]]; then 2567 DIFFOPTS="-t" 2568 else 2569 DIFFOPTS="-bt" 2570 fi 2571 # 2572 # First pass through the files: generate the per-file webrev HTML-files. 2573 # 2574 while read LINE 2575 do 2576 set - $LINE 2577 P=$1 2578 2579 if [[ $1 == "Revision:" ]]; then 2580 OUTREV=$2 2581 continue 2582 fi 2583 # 2584 # Normally, each line in the file list is just a pathname of a 2585 # file that has been modified or created in the child. A file 2586 # that is renamed in the child workspace has two names on the 2587 # line: new name followed by the old name. 2588 # 2589 oldname="" 2590 oldpath="" 2591 rename= 2592 if [[ $# -eq 2 ]]; then 2593 PP=$2 # old filename 2594 oldname=" (was $PP)" 2595 oldpath="$PP" 2596 rename=1 2597 PDIR=${PP%/*} 2598 if [[ $PDIR == $PP ]]; then 2599 PDIR="." # File at root of workspace 2600 fi 2601 2602 PF=${PP##*/} 2603 2604 DIR=${P%/*} 2605 if [[ $DIR == $P ]]; then 2606 DIR="." # File at root of workspace 2607 fi 2608 2609 F=${P##*/} 2610 else 2611 DIR=${P%/*} 2612 if [[ "$DIR" == "$P" ]]; then 2613 DIR="." # File at root of workspace 2614 fi 2615 2616 F=${P##*/} 2617 2618 PP=$P 2619 PDIR=$DIR 2620 PF=$F 2621 fi 2622 2623 # Make the webrev directory if necessary as it may have been 2624 # removed because it was empty 2625 if [ ! -d $CWS/$DIR ]; then 2626 mkdir -p $CWS/$DIR 2627 fi 2628 2629 COMM=`getcomments html $P $PP` 2630 2631 print "\t$P$oldname\n\t\t\c" 2632 2633 # Make the webrev mirror directory if necessary 2634 mkdir -p $WDIR/$DIR 2635 2636 # cd to the directory so the names are short 2637 cd $CWS/$DIR 2638 2639 # 2640 # If we're in OpenSolaris mode, we enforce a minor policy: 2641 # help to make sure the reviewer doesn't accidentally publish 2642 # source which is in usr/closed/* 2643 # 2644 if [[ -n $Oflag ]]; then 2645 pclosed=${P##usr/closed/} 2646 if [[ $pclosed != $P ]]; then 2647 print "*** Omitting closed source for OpenSolaris" \ 2648 "mode review" 2649 continue 2650 fi 2651 fi 2652 2653 # 2654 # We stash old and new files into parallel directories in /tmp 2655 # and do our diffs there. This makes it possible to generate 2656 # clean looking diffs which don't have absolute paths present. 2657 # 2658 olddir=$WDIR/raw_files/old 2659 newdir=$WDIR/raw_files/new 2660 mkdir -p $olddir 2661 mkdir -p $newdir 2662 mkdir -p $olddir/$PDIR 2663 mkdir -p $newdir/$DIR 2664 2665 build_old_new $olddir $newdir $DIR $F 2666 2667 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then 2668 print "*** Error: file not in parent or child" 2669 continue 2670 fi 2671 2672 cd $WDIR/raw_files 2673 ofile=old/$PDIR/$PF 2674 nfile=new/$DIR/$F 2675 2676 mv_but_nodiff= 2677 cmp $ofile $nfile > /dev/null 2>&1 2678 if [[ $? == 0 && $rename == 1 ]]; then 2679 mv_but_nodiff=1 2680 fi 2681 2682 # 2683 # Cleaning up 2684 # 2685 rm -f $WDIR/$DIR/$F.cdiff.html 2686 rm -f $WDIR/$DIR/$F.udiff.html 2687 rm -f $WDIR/$DIR/$F.wdiff.html 2688 rm -f $WDIR/$DIR/$F.sdiff.html 2689 rm -f $WDIR/$DIR/$F-.html 2690 rm -f $WDIR/$DIR/$F.html 2691 2692 its_a_jar= 2693 if expr $F : '.*\.jar' >/dev/null; then 2694 its_a_jar=1 2695 # It's a JAR file, let's do it differntly 2696 if [[ -z $JAR ]]; then 2697 print "No access to jar, so can't produce diffs for jar files" 2698 else 2699 if [ -f $ofile ]; then 2700 $JAR -tvf $ofile >"$ofile".lst 2701 fi 2702 if [ -f $nfile ]; then 2703 $JAR -tvf $nfile >"$nfile".lst 2704 fi 2705 2706 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2707 2708 ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff 2709 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2710 > $WDIR/$DIR/$F.cdiff.html 2711 print " cdiffs\c" 2712 2713 ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff 2714 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2715 > $WDIR/$DIR/$F.udiff.html 2716 2717 print " udiffs\c" 2718 2719 if [[ -x $WDIFF ]]; then 2720 $WDIFF -c "$COMM" \ 2721 -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \ 2722 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2723 if [[ $? -eq 0 ]]; then 2724 print " wdiffs\c" 2725 else 2726 print " wdiffs[fail]\c" 2727 fi 2728 fi 2729 2730 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2731 > $WDIR/$DIR/$F.sdiff.html 2732 print " sdiffs\c" 2733 2734 print " frames\c" 2735 2736 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2737 2738 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count 2739 2740 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2741 # renamed file: may also have differences 2742 difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count 2743 elif [[ -f $nfile ]]; then 2744 # new file: count added lines 2745 difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count 2746 elif [[ -f $ofile ]]; then 2747 # old file: count deleted lines 2748 difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count 2749 fi 2750 fi 2751 else 2752 2753 # 2754 # If we have old and new versions of the file then run the 2755 # appropriate diffs. This is complicated by a couple of factors: 2756 # 2757 # - renames must be handled specially: we emit a 'remove' 2758 # diff and an 'add' diff 2759 # - new files and deleted files must be handled specially 2760 # - Solaris patch(1m) can't cope with file creation 2761 # (and hence renames) as of this writing. 2762 # - To make matters worse, gnu patch doesn't interpret the 2763 # output of Solaris diff properly when it comes to 2764 # adds and deletes. We need to do some "cleansing" 2765 # transformations: 2766 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ 2767 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ 2768 # 2769 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" 2770 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" 2771 2772 rm -f $WDIR/$DIR/$F.patch 2773 if [[ -z $rename ]]; then 2774 if [ ! -f $ofile ]; then 2775 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2776 > $WDIR/$DIR/$F.patch 2777 elif [ ! -f $nfile ]; then 2778 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2779 > $WDIR/$DIR/$F.patch 2780 else 2781 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch 2782 fi 2783 else 2784 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2785 > $WDIR/$DIR/$F.patch 2786 2787 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2788 >> $WDIR/$DIR/$F.patch 2789 2790 fi 2791 2792 2793 # 2794 # Tack the patch we just made onto the accumulated patch for the 2795 # whole wad. 2796 # 2797 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch 2798 2799 print " patch\c" 2800 2801 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2802 2803 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff 2804 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2805 > $WDIR/$DIR/$F.cdiff.html 2806 print " cdiffs\c" 2807 2808 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff 2809 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2810 > $WDIR/$DIR/$F.udiff.html 2811 2812 print " udiffs\c" 2813 2814 if [[ -x $WDIFF ]]; then 2815 $WDIFF -c "$COMM" \ 2816 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ 2817 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2818 if [[ $? -eq 0 ]]; then 2819 print " wdiffs\c" 2820 else 2821 print " wdiffs[fail]\c" 2822 fi 2823 fi 2824 2825 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2826 > $WDIR/$DIR/$F.sdiff.html 2827 print " sdiffs\c" 2828 2829 print " frames\c" 2830 2831 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2832 2833 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2834 2835 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2836 # renamed file: may also have differences 2837 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2838 elif [[ -f $nfile ]]; then 2839 # new file: count added lines 2840 difflines /dev/null $nfile > $WDIR/$DIR/$F.count 2841 elif [[ -f $ofile ]]; then 2842 # old file: count deleted lines 2843 difflines $ofile /dev/null > $WDIR/$DIR/$F.count 2844 fi 2845 fi 2846 # 2847 # Now we generate the postscript for this file. We generate diffs 2848 # only in the event that there is delta, or the file is new (it seems 2849 # tree-killing to print out the contents of deleted files). 2850 # 2851 if [[ -f $nfile ]]; then 2852 ocr=$ofile 2853 [[ ! -f $ofile ]] && ocr=/dev/null 2854 2855 if [[ -z $mv_but_nodiff ]]; then 2856 textcomm=`getcomments text $P $PP` 2857 if [[ -x $CODEREVIEW ]]; then 2858 $CODEREVIEW -y "$textcomm" \ 2859 -e $ocr $nfile \ 2860 > /tmp/$$.psfile 2>/dev/null && 2861 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps 2862 if [[ $? -eq 0 ]]; then 2863 print " ps\c" 2864 else 2865 print " ps[fail]\c" 2866 fi 2867 fi 2868 fi 2869 fi 2870 2871 if [[ -f $ofile && -z $mv_but_nodiff ]]; then 2872 if [[ -n $its_a_jar ]]; then 2873 source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html 2874 else 2875 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html 2876 fi 2877 print " old\c" 2878 fi 2879 2880 if [[ -f $nfile ]]; then 2881 if [[ -n $its_a_jar ]]; then 2882 source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html 2883 else 2884 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html 2885 fi 2886 print " new\c" 2887 fi 2888 2889 print 2890 done < $FLIST 2891 2892 frame_nav_js > $WDIR/ancnav.js 2893 frame_navigation > $WDIR/ancnav.html 2894 2895 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then 2896 print " Generating PDF: \c" 2897 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf 2898 print "Done." 2899 fi 2900 2901 # Now build the index.html file that contains 2902 # links to the source files and their diffs. 2903 2904 cd $CWS 2905 2906 # Save total changed lines for Code Inspection. 2907 print "$TOTL" > $WDIR/TotalChangedLines 2908 2909 print " index.html: \c" 2910 INDEXFILE=$WDIR/index.html 2911 exec 3<&1 # duplicate stdout to FD3. 2912 exec 1<&- # Close stdout. 2913 exec > $INDEXFILE # Open stdout to index file. 2914 2915 print "$HTML<head>" 2916 print "<meta name=\"scm\" content=\"$SCM_MODE\" />" 2917 print "$STDHEAD" 2918 print "<title>$WNAME</title>" 2919 print "</head>" 2920 print "<body id=\"SUNWwebrev\">" 2921 print "<div class=\"summary\">" 2922 print "<h2>Code Review for $WNAME</h2>" 2923 2924 print "<table>" 2925 2926 if [[ -z $uflag ]] 2927 then 2928 if [[ $SCM_MODE == "mercurial" ]] 2929 then 2930 # 2931 # Let's try to extract the user name from the .hgrc file 2932 # 2933 username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'` 2934 fi 2935 2936 if [[ -z $username ]] 2937 then 2938 # 2939 # Figure out the username and gcos name. To maintain compatibility 2940 # with passwd(4), we must support '&' substitutions. 2941 # 2942 username=`id | cut -d '(' -f 2 | cut -d ')' -f 1` 2943 if [[ -x $GETENT ]]; then 2944 realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1` 2945 fi 2946 userupper=`print "$username" | sed 's/\<./\u&/g'` 2947 realname=`print $realname | sed s/\&/$userupper/` 2948 fi 2949 fi 2950 2951 date="on `date`" 2952 2953 if [[ -n "$username" && -n "$realname" ]]; then 2954 print "<tr><th>Prepared by:</th>" 2955 print "<td>$realname ($username) $date</td></tr>" 2956 elif [[ -n "$username" ]]; then 2957 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>" 2958 fi 2959 2960 print "<tr><th>Workspace:</th><td>$CWS</td></tr>" 2961 if [[ -n $parent_webrev ]]; then 2962 print "<tr><th>Compare against:</th><td>" 2963 print "webrev at $parent_webrev" 2964 else 2965 if [[ -n $OUTPWS2 ]]; then 2966 print "<tr><th>Compare against:</th><td>" 2967 print "$OUTPWS2" 2968 fi 2969 fi 2970 print "</td></tr>" 2971 if [[ -n $rflag ]]; then 2972 print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>" 2973 elif [[ -n $OUTREV ]]; then 2974 if [[ -z $forestflag ]]; then 2975 print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>" 2976 fi 2977 fi 2978 if [[ -n $HG_BRANCH ]]; then 2979 print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>" 2980 fi 2981 2982 print "<tr><th>Summary of changes:</th><td>" 2983 printCI $TOTL $TINS $TDEL $TMOD $TUNC 2984 print "</td></tr>" 2985 2986 if [[ -f $WDIR/$WNAME.patch ]]; then 2987 print "<tr><th>Patch of changes:</th><td>" 2988 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>" 2989 fi 2990 if [[ -f $WDIR/$WNAME.pdf ]]; then 2991 print "<tr><th>Printable review:</th><td>" 2992 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>" 2993 fi 2994 2995 if [[ -n "$iflag" ]]; then 2996 print "<tr><th>Author comments:</th><td><div>" 2997 cat /tmp/$$.include 2998 print "</div></td></tr>" 2999 fi 3000 # Add links to referenced CRs, if any 3001 # external URL has a <title> like: 3002 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title> 3003 # while internal URL has <title> like: 3004 # <title>6641309: Wrong Cookie separator used in HttpURLConnection</title> 3005 # 3006 if [[ -n $CRID ]]; then 3007 for id in $CRID 3008 do 3009 print "<tr><th>Bug id:</th><td>" 3010 url="${BUGURL}${id}" 3011 if [[ -n $WGET ]]; then 3012 msg=`$WGET -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed 's/Bug ID://'` 3013 fi 3014 if [[ -n $msg ]]; then 3015 print "<a href=\"$url\">$msg</a>" 3016 else 3017 print $id | bug2url 3018 fi 3019 3020 print "</td></tr>" 3021 done 3022 fi 3023 print "<tr><th>Legend:</th><td>" 3024 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>" 3025 print "</table>" 3026 print "</div>" 3027 3028 # 3029 # Second pass through the files: generate the rest of the index file 3030 # 3031 while read LINE 3032 do 3033 set - $LINE 3034 if [[ $1 == "Revision:" ]]; then 3035 FIRST_CREV=`expr $3 + 1` 3036 continue 3037 fi 3038 P=$1 3039 3040 if [[ $# == 2 ]]; then 3041 PP=$2 3042 oldname=" <i>(was $PP)</i>" 3043 3044 else 3045 PP=$P 3046 oldname="" 3047 fi 3048 3049 DIR=${P%/*} 3050 if [[ $DIR == $P ]]; then 3051 DIR="." # File at root of workspace 3052 fi 3053 3054 # Avoid processing the same file twice. 3055 # It's possible for renamed files to 3056 # appear twice in the file list 3057 3058 F=$WDIR/$P 3059 3060 print "<p><code>" 3061 3062 # If there's a diffs file, make diffs links 3063 3064 NODIFFS= 3065 if [[ -f $F.cdiff.html ]]; then 3066 print "<a href=\"$P.cdiff.html\">Cdiffs</a>" 3067 print "<a href=\"$P.udiff.html\">Udiffs</a>" 3068 3069 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 3070 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 3071 fi 3072 3073 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 3074 3075 print "<a href=\"$P.frames.html\">Frames</a>" 3076 else 3077 NODIFFS=1 3078 print " ------ ------ ------" 3079 3080 if [[ -x $WDIFF ]]; then 3081 print " ------" 3082 fi 3083 3084 print " ------" 3085 fi 3086 3087 # If there's an old file, make the link 3088 3089 NOOLD= 3090 if [[ -f $F-.html ]]; then 3091 print "<a href=\"$P-.html\">Old</a>" 3092 else 3093 NOOLD=1 3094 print " ---" 3095 fi 3096 3097 # If there's an new file, make the link 3098 3099 NONEW= 3100 if [[ -f $F.html ]]; then 3101 print "<a href=\"$P.html\">New</a>" 3102 else 3103 NONEW=1 3104 print " ---" 3105 fi 3106 3107 if [[ -f $F.patch ]]; then 3108 print "<a href=\"$P.patch\">Patch</a>" 3109 else 3110 print " -----" 3111 fi 3112 3113 if [[ -f $WDIR/raw_files/new/$P ]]; then 3114 print "<a href=\"raw_files/new/$P\">Raw</a>" 3115 else 3116 print " ---" 3117 fi 3118 print "</code>" 3119 if [[ -n $NODIFFS && -z $oldname ]]; then 3120 if [[ -n $NOOLD ]]; then 3121 print "<font color=green><b>$P</b></font>" 3122 elif [[ -n $NONEW ]]; then 3123 print "<font color=red><b>$P</b></font>" 3124 fi 3125 else 3126 print "<b>$P</b> $oldname" 3127 fi 3128 3129 # 3130 # Check for usr/closed 3131 # 3132 if [ ! -z "$Oflag" ]; then 3133 if [[ $P == usr/closed/* ]]; then 3134 print " <i>Closed source: omitted from" \ 3135 "this review</i>" 3136 fi 3137 fi 3138 3139 print "</p><blockquote>\c" 3140 # Insert delta comments if any 3141 comments=`getcomments html $P $PP` 3142 if [ -n "$comments" ]; then 3143 print "<pre>$comments</pre>" 3144 fi 3145 3146 # Add additional comments comment 3147 3148 print "<!-- Add comments to explain changes in $P here -->" 3149 3150 # Add count of changes. 3151 3152 if [[ -f $F.count ]]; then 3153 cat $F.count 3154 rm $F.count 3155 fi 3156 print "</blockquote>" 3157 done < $FLIST 3158 3159 print 3160 print 3161 print "<hr />" 3162 print "<p style=\"font-size: small\">" 3163 print "This code review page was prepared using <b>$0</b>" 3164 print "(vers $WEBREV_UPDATED).</p>" 3165 print "</body>" 3166 print "</html>" 3167 3168 if [[ -n $ZIP ]]; then 3169 # Let's generate a zip file for convenience 3170 cd $WDIR/.. 3171 if [ -f webrev.zip ]; then 3172 rm webrev.zip 3173 fi 3174 $ZIP -r webrev webrev >/dev/null 2>&1 3175 fi 3176 3177 exec 1<&- # Close FD 1. 3178 exec 1<&3 # dup FD 3 to restore stdout. 3179 exec 3<&- # close FD 3. 3180 3181 print "Done." 3182 print "Output to: $WDIR" 3183