1 #!/usr/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 23 # 24 # Copyright 2008 Sun Microsystems, Inc. All rights reserved. 25 # Use is subject to license terms. 26 # 27 28 # 29 # This script takes a file list and a workspace and builds a set of html files 30 # suitable for doing a code review of source changes via a web page. 31 # Documentation is available via the manual page, webrev.1, or just 32 # type 'webrev -h'. 33 # 34 # Acknowledgements to contributors to webrev are listed in the webrev(1) 35 # man page. 36 # 37 38 REMOVED_COLOR=brown 39 CHANGED_COLOR=blue 40 NEW_COLOR=blue 41 42 HTML='<?xml version="1.0"?> 43 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 44 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 45 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' 46 47 FRAMEHTML='<?xml version="1.0"?> 48 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" 49 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> 50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' 51 52 STDHEAD='<meta http-equiv="cache-control" content="no-cache"></meta> 53 <meta http-equiv="Pragma" content="no-cache"></meta> 54 <meta http-equiv="Expires" content="-1"></meta> 55 <!-- 56 Note to customizers: the body of the webrev is IDed as SUNWwebrev 57 to allow easy overriding by users of webrev via the userContent.css 58 mechanism available in some browsers. 59 60 For example, to have all "removed" information be red instead of 61 brown, set a rule in your userContent.css file like: 62 63 body#SUNWwebrev span.removed { color: red ! important; } 64 --> 65 <style type="text/css" media="screen"> 66 body { 67 background-color: #eeeeee; 68 } 69 hr { 70 border: none 0; 71 border-top: 1px solid #aaa; 72 height: 1px; 73 } 74 div.summary { 75 font-size: .8em; 76 border-bottom: 1px solid #aaa; 77 padding-left: 1em; 78 padding-right: 1em; 79 } 80 div.summary h2 { 81 margin-bottom: 0.3em; 82 } 83 div.summary table th { 84 text-align: right; 85 vertical-align: top; 86 white-space: nowrap; 87 } 88 span.lineschanged { 89 font-size: 0.7em; 90 } 91 span.oldmarker { 92 color: red; 93 font-size: large; 94 font-weight: bold; 95 } 96 span.newmarker { 97 color: green; 98 font-size: large; 99 font-weight: bold; 100 } 101 span.removed { 102 color: brown; 103 } 104 span.changed { 105 color: blue; 106 } 107 span.new { 108 color: blue; 109 font-weight: bold; 110 } 111 span.chmod { 112 font-size: 0.7em; 113 color: #db7800; 114 } 115 a.print { font-size: x-small; } 116 a:hover { background-color: #ffcc99; } 117 </style> 118 119 <style type="text/css" media="print"> 120 pre { font-size: 0.8em; font-family: courier, monospace; } 121 span.removed { color: #444; font-style: italic } 122 span.changed { font-weight: bold; } 123 span.new { font-weight: bold; } 124 span.newmarker { font-size: 1.2em; font-weight: bold; } 125 span.oldmarker { font-size: 1.2em; font-weight: bold; } 126 a.print {display: none} 127 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; } 128 </style> 129 ' 130 131 # 132 # UDiffs need a slightly different CSS rule for 'new' items (we don't 133 # want them to be bolded as we do in cdiffs or sdiffs). 134 # 135 UDIFFCSS=' 136 <style type="text/css" media="screen"> 137 span.new { 138 color: blue; 139 font-weight: normal; 140 } 141 </style> 142 ' 143 144 # 145 # input_cmd | html_quote | output_cmd 146 # or 147 # html_quote filename | output_cmd 148 # 149 # Make a piece of source code safe for display in an HTML <pre> block. 150 # 151 html_quote() 152 { 153 sed -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" "$@" | expand 154 } 155 156 # 157 # input_cmd | bug2url | output_cmd 158 # 159 # Scan for bugids and insert <a> links to the relevent bug database. 160 # 161 bug2url() 162 { 163 sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL'&\">&</a>|g' 164 } 165 166 # 167 # input_cmd | sac2url | output_cmd 168 # 169 # Scan for ARC cases and insert <a> links to the relevent SAC database. 170 # This is slightly complicated because inside the SWAN, SAC cases are 171 # grouped by ARC: PSARC/2006/123. But on OpenSolaris.org, they are 172 # referenced as 2006/123 (without labelling the ARC). 173 # 174 sac2url() 175 { 176 if [[ -z "$Oflag" ]]; then 177 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' 178 else 179 sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'/\2/\3\">\1 \2/\3</a>|g' 180 fi 181 } 182 183 # 184 # strip_unchanged <infile> | output_cmd 185 # 186 # Removes chunks of sdiff documents that have not changed. This makes it 187 # easier for a code reviewer to find the bits that have changed. 188 # 189 # Deleted lines of text are replaced by a horizontal rule. Some 190 # identical lines are retained before and after the changed lines to 191 # provide some context. The number of these lines is controlled by the 192 # variable C in the $AWK script below. 193 # 194 # The script detects changed lines as any line that has a "<span class=" 195 # string embedded (unchanged lines have no particular class and are not 196 # part of a <span>). Blank lines (without a sequence number) are also 197 # detected since they flag lines that have been inserted or deleted. 198 # 199 strip_unchanged() 200 { 201 $AWK ' 202 BEGIN { C = c = 20 } 203 NF == 0 || /<span class="/ { 204 if (c > C) { 205 c -= C 206 inx = 0 207 if (c > C) { 208 print "\n</pre><hr></hr><pre>" 209 inx = c % C 210 c = C 211 } 212 213 for (i = 0; i < c; i++) 214 print ln[(inx + i) % C] 215 } 216 c = 0; 217 print 218 next 219 } 220 { if (c >= C) { 221 ln[c % C] = $0 222 c++; 223 next; 224 } 225 c++; 226 print 227 } 228 END { if (c > (C * 2)) print "\n</pre><hr></hr>" } 229 230 ' $1 231 } 232 233 # 234 # sdiff_to_html 235 # 236 # This function takes two files as arguments, obtains their diff, and 237 # processes the diff output to present the files as an HTML document with 238 # the files displayed side-by-side, differences shown in color. It also 239 # takes a delta comment, rendered as an HTML snippet, as the third 240 # argument. The function takes two files as arguments, then the name of 241 # file, the path, and the comment. The HTML will be delivered on stdout, 242 # e.g. 243 # 244 # $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \ 245 # new/usr/src/tools/scripts/webrev.sh \ 246 # webrev.sh usr/src/tools/scripts \ 247 # '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567"> 248 # 1234567</a> my bugid' > <file>.html 249 # 250 # framed_sdiff() is then called which creates $2.frames.html 251 # in the webrev tree. 252 # 253 # FYI: This function is rather unusual in its use of awk. The initial 254 # diff run produces conventional diff output showing changed lines mixed 255 # with editing codes. The changed lines are ignored - we're interested in 256 # the editing codes, e.g. 257 # 258 # 8c8 259 # 57a61 260 # 63c66,76 261 # 68,93d80 262 # 106d90 263 # 108,110d91 264 # 265 # These editing codes are parsed by the awk script and used to generate 266 # another awk script that generates HTML, e.g the above lines would turn 267 # into something like this: 268 # 269 # BEGIN { printf "<pre>\n" } 270 # function sp(n) {for (i=0;i<n;i++)printf "\n"} 271 # function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0} 272 # NR==8 {wl("#7A7ADD");next} 273 # NR==54 {wl("#7A7ADD");sp(3);next} 274 # NR==56 {wl("#7A7ADD");next} 275 # NR==57 {wl("black");printf "\n"; next} 276 # : : 277 # 278 # This script is then run on the original source file to generate the 279 # HTML that corresponds to the source file. 280 # 281 # The two HTML files are then combined into a single piece of HTML that 282 # uses an HTML table construct to present the files side by side. You'll 283 # notice that the changes are color-coded: 284 # 285 # black - unchanged lines 286 # blue - changed lines 287 # bold blue - new lines 288 # brown - deleted lines 289 # 290 # Blank lines are inserted in each file to keep unchanged lines in sync 291 # (side-by-side). This format is familiar to users of sdiff(1) or 292 # Teamware's filemerge tool. 293 # 294 sdiff_to_html() 295 { 296 diff -b $1 $2 > /tmp/$$.diffs 297 298 TNAME=$3 299 TPATH=$4 300 COMMENT=$5 301 302 # 303 # Now we have the diffs, generate the HTML for the old file. 304 # 305 $AWK ' 306 BEGIN { 307 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n" 308 printf "function removed() " 309 printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n" 310 printf "function changed() " 311 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n" 312 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n" 313 } 314 /^</ {next} 315 /^>/ {next} 316 /^---/ {next} 317 318 { 319 split($1, a, /[cad]/) ; 320 if (index($1, "a")) { 321 if (a[1] == 0) { 322 n = split(a[2], r, /,/); 323 if (n == 1) 324 printf "BEGIN\t\t{sp(1)}\n" 325 else 326 printf "BEGIN\t\t{sp(%d)}\n",\ 327 (r[2] - r[1]) + 1 328 next 329 } 330 331 printf "NR==%s\t\t{", a[1] 332 n = split(a[2], r, /,/); 333 s = r[1]; 334 if (n == 1) 335 printf "bl();printf \"\\n\"; next}\n" 336 else { 337 n = r[2] - r[1] 338 printf "bl();sp(%d);next}\n",\ 339 (r[2] - r[1]) + 1 340 } 341 next 342 } 343 if (index($1, "d")) { 344 n = split(a[1], r, /,/); 345 n1 = r[1] 346 n2 = r[2] 347 if (n == 1) 348 printf "NR==%s\t\t{removed(); next}\n" , n1 349 else 350 printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2 351 next 352 } 353 if (index($1, "c")) { 354 n = split(a[1], r, /,/); 355 n1 = r[1] 356 n2 = r[2] 357 final = n2 358 d1 = 0 359 if (n == 1) 360 printf "NR==%s\t\t{changed();" , n1 361 else { 362 d1 = n2 - n1 363 printf "NR==%s,NR==%s\t{changed();" , n1, n2 364 } 365 m = split(a[2], r, /,/); 366 n1 = r[1] 367 n2 = r[2] 368 if (m > 1) { 369 d2 = n2 - n1 370 if (d2 > d1) { 371 if (n > 1) printf "if (NR==%d)", final 372 printf "sp(%d);", d2 - d1 373 } 374 } 375 printf "next}\n" ; 376 377 next 378 } 379 } 380 381 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" } 382 ' /tmp/$$.diffs > /tmp/$$.file1 383 384 # 385 # Now generate the HTML for the new file 386 # 387 $AWK ' 388 BEGIN { 389 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n" 390 printf "function new() " 391 printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n" 392 printf "function changed() " 393 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n" 394 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n" 395 } 396 397 /^</ {next} 398 /^>/ {next} 399 /^---/ {next} 400 401 { 402 split($1, a, /[cad]/) ; 403 if (index($1, "d")) { 404 if (a[2] == 0) { 405 n = split(a[1], r, /,/); 406 if (n == 1) 407 printf "BEGIN\t\t{sp(1)}\n" 408 else 409 printf "BEGIN\t\t{sp(%d)}\n",\ 410 (r[2] - r[1]) + 1 411 next 412 } 413 414 printf "NR==%s\t\t{", a[2] 415 n = split(a[1], r, /,/); 416 s = r[1]; 417 if (n == 1) 418 printf "bl();printf \"\\n\"; next}\n" 419 else { 420 n = r[2] - r[1] 421 printf "bl();sp(%d);next}\n",\ 422 (r[2] - r[1]) + 1 423 } 424 next 425 } 426 if (index($1, "a")) { 427 n = split(a[2], r, /,/); 428 n1 = r[1] 429 n2 = r[2] 430 if (n == 1) 431 printf "NR==%s\t\t{new() ; next}\n" , n1 432 else 433 printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2 434 next 435 } 436 if (index($1, "c")) { 437 n = split(a[2], r, /,/); 438 n1 = r[1] 439 n2 = r[2] 440 final = n2 441 d2 = 0; 442 if (n == 1) { 443 final = n1 444 printf "NR==%s\t\t{changed();" , n1 445 } else { 446 d2 = n2 - n1 447 printf "NR==%s,NR==%s\t{changed();" , n1, n2 448 } 449 m = split(a[1], r, /,/); 450 n1 = r[1] 451 n2 = r[2] 452 if (m > 1) { 453 d1 = n2 - n1 454 if (d1 > d2) { 455 if (n > 1) printf "if (NR==%d)", final 456 printf "sp(%d);", d1 - d2 457 } 458 } 459 printf "next}\n" ; 460 next 461 } 462 } 463 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" } 464 ' /tmp/$$.diffs > /tmp/$$.file2 465 466 # 467 # Post-process the HTML files by running them back through $AWK 468 # 469 html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html 470 471 html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html 472 473 # 474 # Now combine into a valid HTML file and side-by-side into a table 475 # 476 print "$HTML<head>$STDHEAD" 477 print "<title>$WNAME Sdiff $TPATH/$TNAME</title>" 478 print "</head><body id=\"SUNWwebrev\">" 479 print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>" 480 print "<pre>$COMMENT</pre>\n" 481 print "<table><tr valign=\"top\">" 482 print "<td><pre>" 483 484 strip_unchanged /tmp/$$.file1.html 485 486 print "</pre></td><td><pre>" 487 488 strip_unchanged /tmp/$$.file2.html 489 490 print "</pre></td>" 491 print "</tr></table>" 492 print "</body></html>" 493 494 framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \ 495 "$COMMENT" 496 } 497 498 499 # 500 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment> 501 # 502 # Expects lefthand and righthand side html files created by sdiff_to_html. 503 # We use insert_anchors() to augment those with HTML navigation anchors, 504 # and then emit the main frame. Content is placed into: 505 # 506 # $WDIR/DIR/$TNAME.lhs.html 507 # $WDIR/DIR/$TNAME.rhs.html 508 # $WDIR/DIR/$TNAME.frames.html 509 # 510 # NOTE: We rely on standard usage of $WDIR and $DIR. 511 # 512 function framed_sdiff 513 { 514 typeset TNAME=$1 515 typeset TPATH=$2 516 typeset lhsfile=$3 517 typeset rhsfile=$4 518 typeset comments=$5 519 typeset RTOP 520 521 # Enable html files to access WDIR via a relative path. 522 RTOP=$(relative_dir $TPATH $WDIR) 523 524 # Make the rhs/lhs files and output the frameset file. 525 print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html 526 527 cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF 528 <script type="text/javascript" src="$RTOP/ancnav.js"></script> 529 </head> 530 <body id="SUNWwebrev" onkeypress="keypress(event);"> 531 <a name="0"></a> 532 <pre>$comments</pre><hr></hr> 533 EOF 534 535 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html 536 537 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html 538 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html 539 540 close='</body></html>' 541 542 print $close >> $WDIR/$DIR/$TNAME.lhs.html 543 print $close >> $WDIR/$DIR/$TNAME.rhs.html 544 545 print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html 546 print "<title>$WNAME Framed-Sdiff " \ 547 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html 548 cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF 549 <frameset rows="*,60"> 550 <frameset cols="50%,50%"> 551 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs"></frame> 552 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame> 553 </frameset> 554 <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0" 555 marginheight="0" name="nav"></frame> 556 <noframes> 557 <body id="SUNWwebrev"> 558 Alas 'frames' webrev requires that your browser supports frames 559 and has the feature enabled. 560 </body> 561 </noframes> 562 </frameset> 563 </html> 564 EOF 565 } 566 567 568 # 569 # fix_postscript 570 # 571 # Merge codereview output files to a single conforming postscript file, by: 572 # - removing all extraneous headers/trailers 573 # - making the page numbers right 574 # - removing pages devoid of contents which confuse some 575 # postscript readers. 576 # 577 # From Casper. 578 # 579 function fix_postscript 580 { 581 infile=$1 582 583 cat > /tmp/$$.crmerge.pl << \EOF 584 585 print scalar(<>); # %!PS-Adobe--- 586 print "%%Orientation: Landscape\n"; 587 588 $pno = 0; 589 $doprint = 1; 590 591 $page = ""; 592 593 while (<>) { 594 next if (/^%%Pages:\s*\d+/); 595 596 if (/^%%Page:/) { 597 if ($pno == 0 || $page =~ /\)S/) { 598 # Header or single page containing text 599 print "%%Page: ? $pno\n" if ($pno > 0); 600 print $page; 601 $pno++; 602 } else { 603 # Empty page, skip it. 604 } 605 $page = ""; 606 $doprint = 1; 607 next; 608 } 609 610 # Skip from %%Trailer of one document to Endprolog 611 # %%Page of the next 612 $doprint = 0 if (/^%%Trailer/); 613 $page .= $_ if ($doprint); 614 } 615 616 if ($page =~ /\)S/) { 617 print "%%Page: ? $pno\n"; 618 print $page; 619 } else { 620 $pno--; 621 } 622 print "%%Trailer\n%%Pages: $pno\n"; 623 EOF 624 625 $PERL /tmp/$$.crmerge.pl < $infile 626 } 627 628 629 # 630 # input_cmd | insert_anchors | output_cmd 631 # 632 # Flag blocks of difference with sequentially numbered invisible 633 # anchors. These are used to drive the frames version of the 634 # sdiffs output. 635 # 636 # NOTE: Anchor zero flags the top of the file irrespective of changes, 637 # an additional anchor is also appended to flag the bottom. 638 # 639 # The script detects changed lines as any line that has a "<span 640 # class=" string embedded (unchanged lines have no class set and are 641 # not part of a <span>. Blank lines (without a sequence number) 642 # are also detected since they flag lines that have been inserted or 643 # deleted. 644 # 645 function insert_anchors 646 { 647 $AWK ' 648 function ia() { 649 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++; 650 } 651 652 BEGIN { 653 anc=1; 654 inblock=1; 655 printf "<pre>\n"; 656 } 657 NF == 0 || /^<span class=/ { 658 if (inblock == 0) { 659 ia(); 660 inblock=1; 661 } 662 print; 663 next; 664 } 665 { 666 inblock=0; 667 print; 668 } 669 END { 670 ia(); 671 672 printf "<b style=\"font-size: large; color: red\">"; 673 printf "--- EOF ---</b>" 674 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n"; 675 printf "</pre>" 676 printf "<form name=\"eof\">"; 677 printf "<input name=\"value\" value=\"%d\" " \ 678 "type=\"hidden\"></input>", anc - 1; 679 printf "</form>"; 680 } 681 ' $1 682 } 683 684 685 # 686 # relative_dir 687 # 688 # Print a relative return path from $1 to $2. For example if 689 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview, 690 # this function would print "../../../../". 691 # 692 # In the event that $1 is not in $2 a warning is printed to stderr, 693 # and $2 is returned-- the result of this is that the resulting webrev 694 # is not relocatable. 695 # 696 function relative_dir 697 { 698 typeset cur="${1##$2?(/)}" 699 typeset ret="" 700 if [[ $2 == $cur ]]; then # Should never happen. 701 # Should never happen. 702 print -u2 "\nWARNING: relative_dir: \"$1\" not relative " 703 print -u2 "to \"$2\". Check input paths. Framed webrev " 704 print -u2 "will not be relocatable!" 705 print $2 706 return 707 fi 708 709 while [[ -n ${cur} ]]; 710 do 711 cur=${cur%%*(/)*([!/])} 712 if [[ -z $ret ]]; then 713 ret=".." 714 else 715 ret="../$ret" 716 fi 717 done 718 print $ret 719 } 720 721 722 # 723 # frame_nav_js 724 # 725 # Emit javascript for frame navigation 726 # 727 function frame_nav_js 728 { 729 cat << \EOF 730 var myInt; 731 var scrolling=0; 732 var sfactor = 3; 733 var scount=10; 734 735 function scrollByPix() { 736 if (scount<=0) { 737 sfactor*=1.2; 738 scount=10; 739 } 740 parent.lhs.scrollBy(0,sfactor); 741 parent.rhs.scrollBy(0,sfactor); 742 scount--; 743 } 744 745 function scrollToAnc(num) { 746 747 // Update the value of the anchor in the form which we use as 748 // storage for this value. setAncValue() will take care of 749 // correcting for overflow and underflow of the value and return 750 // us the new value. 751 num = setAncValue(num); 752 753 // Set location and scroll back a little to expose previous 754 // lines. 755 // 756 // Note that this could be improved: it is possible although 757 // complex to compute the x and y position of an anchor, and to 758 // scroll to that location directly. 759 // 760 parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num); 761 parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num); 762 763 parent.lhs.scrollBy(0,-30); 764 parent.rhs.scrollBy(0,-30); 765 } 766 767 function getAncValue() 768 { 769 return (parseInt(parent.nav.document.diff.real.value)); 770 } 771 772 function setAncValue(val) 773 { 774 if (val <= 0) { 775 val = 0; 776 parent.nav.document.diff.real.value = val; 777 parent.nav.document.diff.display.value = "BOF"; 778 return (val); 779 } 780 781 // 782 // The way we compute the max anchor value is to stash it 783 // inline in the left and right hand side pages-- it's the same 784 // on each side, so we pluck from the left. 785 // 786 maxval = parent.lhs.document.eof.value.value; 787 if (val < maxval) { 788 parent.nav.document.diff.real.value = val; 789 parent.nav.document.diff.display.value = val.toString(); 790 return (val); 791 } 792 793 // this must be: val >= maxval 794 val = maxval; 795 parent.nav.document.diff.real.value = val; 796 parent.nav.document.diff.display.value = "EOF"; 797 return (val); 798 } 799 800 function stopScroll() { 801 if (scrolling==1) { 802 clearInterval(myInt); 803 scrolling=0; 804 } 805 } 806 807 function startScroll() { 808 stopScroll(); 809 scrolling=1; 810 myInt=setInterval("scrollByPix()",10); 811 } 812 813 function handlePress(b) { 814 815 switch (b) { 816 case 1 : 817 scrollToAnc(-1); 818 break; 819 case 2 : 820 scrollToAnc(getAncValue() - 1); 821 break; 822 case 3 : 823 sfactor=-3; 824 startScroll(); 825 break; 826 case 4 : 827 sfactor=3; 828 startScroll(); 829 break; 830 case 5 : 831 scrollToAnc(getAncValue() + 1); 832 break; 833 case 6 : 834 scrollToAnc(999999); 835 break; 836 } 837 } 838 839 function handleRelease(b) { 840 stopScroll(); 841 } 842 843 function keypress(ev) { 844 var keynum; 845 var keychar; 846 847 if (window.event) { // IE 848 keynum = ev.keyCode; 849 } else if (ev.which) { // non-IE 850 keynum = ev.which; 851 } 852 853 keychar = String.fromCharCode(keynum); 854 855 if (keychar == "k") { 856 handlePress(2); 857 return (0); 858 } else if (keychar == "j" || keychar == " ") { 859 handlePress(5); 860 return (0); 861 } 862 return (1); 863 } 864 865 function ValidateDiffNum(){ 866 val = parent.nav.document.diff.display.value; 867 if (val == "EOF") { 868 scrollToAnc(999999); 869 return; 870 } 871 872 if (val == "BOF") { 873 scrollToAnc(0); 874 return; 875 } 876 877 i=parseInt(val); 878 if (isNaN(i)) { 879 parent.nav.document.diff.display.value = getAncValue(); 880 } else { 881 scrollToAnc(i); 882 } 883 return false; 884 } 885 886 EOF 887 } 888 889 # 890 # frame_navigation 891 # 892 # Output anchor navigation file for framed sdiffs. 893 # 894 function frame_navigation 895 { 896 print "$HTML<head>$STDHEAD" 897 898 cat << \EOF 899 <title>Anchor Navigation</title> 900 <meta http-equiv="Content-Script-Type" content="text/javascript"> 901 <meta http-equiv="Content-Type" content="text/html"> 902 903 <style type="text/css"> 904 div.button td { padding-left: 5px; padding-right: 5px; 905 background-color: #eee; text-align: center; 906 border: 1px #444 outset; cursor: pointer; } 907 div.button a { font-weight: bold; color: black } 908 div.button td:hover { background: #ffcc99; } 909 </style> 910 EOF 911 912 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>" 913 914 cat << \EOF 915 </head> 916 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();" 917 onkeypress="keypress(event);"> 918 <noscript lang="javascript"> 919 <center> 920 <p><big>Framed Navigation controls require Javascript</big><br></br> 921 Either this browser is incompatable or javascript is not enabled</p> 922 </center> 923 </noscript> 924 <table width="100%" border="0" align="center"> 925 <tr> 926 <td valign="middle" width="25%">Diff navigation: 927 Use 'j' and 'k' for next and previous diffs; or use buttons 928 at right</td> 929 <td align="center" valign="top" width="50%"> 930 <div class="button"> 931 <table border="0" align="center"> 932 <tr> 933 <td> 934 <a onMouseDown="handlePress(1);return true;" 935 onMouseUp="handleRelease(1);return true;" 936 onMouseOut="handleRelease(1);return true;" 937 onClick="return false;" 938 title="Go to Beginning Of file">BOF</a></td> 939 <td> 940 <a onMouseDown="handlePress(3);return true;" 941 onMouseUp="handleRelease(3);return true;" 942 onMouseOut="handleRelease(3);return true;" 943 title="Scroll Up: Press and Hold to accelerate" 944 onClick="return false;">Scroll Up</a></td> 945 <td> 946 <a onMouseDown="handlePress(2);return true;" 947 onMouseUp="handleRelease(2);return true;" 948 onMouseOut="handleRelease(2);return true;" 949 title="Go to previous Diff" 950 onClick="return false;">Prev Diff</a> 951 </td></tr> 952 953 <tr> 954 <td> 955 <a onMouseDown="handlePress(6);return true;" 956 onMouseUp="handleRelease(6);return true;" 957 onMouseOut="handleRelease(6);return true;" 958 onClick="return false;" 959 title="Go to End Of File">EOF</a></td> 960 <td> 961 <a onMouseDown="handlePress(4);return true;" 962 onMouseUp="handleRelease(4);return true;" 963 onMouseOut="handleRelease(4);return true;" 964 title="Scroll Down: Press and Hold to accelerate" 965 onClick="return false;">Scroll Down</a></td> 966 <td> 967 <a onMouseDown="handlePress(5);return true;" 968 onMouseUp="handleRelease(5);return true;" 969 onMouseOut="handleRelease(5);return true;" 970 title="Go to next Diff" 971 onClick="return false;">Next Diff</a></td> 972 </tr> 973 </table> 974 </div> 975 </td> 976 <th valign="middle" width="25%"> 977 <form action="" name="diff" onsubmit="return ValidateDiffNum();"> 978 <input name="display" value="BOF" size="8" type="text"></input> 979 <input name="real" value="0" size="8" type="hidden"></input> 980 </form> 981 </th> 982 </tr> 983 </table> 984 </body> 985 </html> 986 EOF 987 } 988 989 990 991 # 992 # diff_to_html <filename> <filepath> { U | C } <comment> 993 # 994 # Processes the output of diff to produce an HTML file representing either 995 # context or unified diffs. 996 # 997 diff_to_html() 998 { 999 TNAME=$1 1000 TPATH=$2 1001 DIFFTYPE=$3 1002 COMMENT=$4 1003 1004 print "$HTML<head>$STDHEAD" 1005 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>" 1006 1007 if [[ $DIFFTYPE == "U" ]]; then 1008 print "$UDIFFCSS" 1009 fi 1010 1011 cat <<-EOF 1012 </head> 1013 <body id="SUNWwebrev"> 1014 <a class="print" href="javascript:print()">Print this page</a> 1015 <pre>$COMMENT</pre> 1016 <pre> 1017 EOF 1018 1019 html_quote | $AWK ' 1020 /^--- new/ { next } 1021 /^\+\+\+ new/ { next } 1022 /^--- old/ { next } 1023 /^\*\*\* old/ { next } 1024 /^\*\*\*\*/ { next } 1025 /^-------/ { printf "<center><h1>%s</h1></center>\n", $0; next } 1026 /^\@\@.*\@\@$/ { printf "</pre><hr></hr><pre>\n"; 1027 printf "<span class=\"newmarker\">%s</span>\n", $0; 1028 next} 1029 1030 /^\*\*\*/ { printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0; 1031 next} 1032 /^---/ { printf "<span class=\"newmarker\">%s</span>\n", $0; 1033 next} 1034 /^\+/ {printf "<span class=\"new\">%s</span>\n", $0; next} 1035 /^!/ {printf "<span class=\"changed\">%s</span>\n", $0; next} 1036 /^-/ {printf "<span class=\"removed\">%s</span>\n", $0; next} 1037 {printf "%s\n", $0; next} 1038 ' 1039 1040 print "</pre></body></html>\n" 1041 } 1042 1043 1044 # 1045 # source_to_html { new | old } <filename> 1046 # 1047 # Process a plain vanilla source file to transform it into an HTML file. 1048 # 1049 source_to_html() 1050 { 1051 WHICH=$1 1052 TNAME=$2 1053 1054 print "$HTML<head>$STDHEAD" 1055 print "<title>$WNAME $WHICH $TNAME</title>" 1056 print "<body id=\"SUNWwebrev\">" 1057 print "<pre>" 1058 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }' 1059 print "</pre></body></html>" 1060 } 1061 1062 # 1063 # comments_from_teamware {text|html} parent-file child-file 1064 # 1065 # Find the first delta in the child that's not in the parent. Get the 1066 # newest delta from the parent, get all deltas from the child starting 1067 # with that delta, and then get all info starting with the second oldest 1068 # delta in that list (the first delta unique to the child). 1069 # 1070 # This code adapted from Bill Shannon's "spc" script 1071 # 1072 comments_from_teamware() 1073 { 1074 fmt=$1 1075 pfile=$PWS/$2 1076 cfile=$CWS/$3 1077 1078 if [[ ! -f $PWS/${2%/*}/SCCS/s.${2##*/} && -n $RWS ]]; then 1079 pfile=$RWS/$2 1080 fi 1081 1082 if [[ -f $pfile ]]; then 1083 psid=$($SCCS prs -d:I: $pfile 2>/dev/null) 1084 else 1085 psid=1.1 1086 fi 1087 1088 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null) 1089 N=${#sids[@]} 1090 1091 nawkprg=' 1092 /^COMMENTS:/ {p=1; continue} 1093 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; } 1094 NF == 0u { continue } 1095 {if (p==0) continue; print $0 }' 1096 1097 if [[ $N -ge 2 ]]; then 1098 sid1=${sids[$((N-2))]} # Gets 2nd to last sid 1099 1100 if [[ $fmt == "text" ]]; then 1101 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1102 $AWK "$nawkprg" 1103 return 1104 fi 1105 1106 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1107 html_quote | bug2url | sac2url | $AWK "$nawkprg" 1108 fi 1109 } 1110 1111 # 1112 # comments_from_wx {text|html} filepath 1113 # 1114 # Given the pathname of a file, find its location in a "wx" active 1115 # file list and print the following comment. Output is either text or 1116 # HTML; if the latter, embedded bugids (sequence of 5 or more digits) 1117 # are turned into URLs. 1118 # 1119 # This is also used with Mercurial and the file list provided by hg-active. 1120 # 1121 comments_from_wx() 1122 { 1123 typeset fmt=$1 1124 typeset p=$2 1125 1126 comm=`$AWK ' 1127 $1 == "'$p'" { 1128 do getline ; while (NF > 0) 1129 getline 1130 while (NF > 0) { print ; getline } 1131 exit 1132 }' < $wxfile` 1133 1134 if [[ -z $comm ]]; then 1135 comm="*** NO COMMENTS ***" 1136 fi 1137 1138 if [[ $fmt == "text" ]]; then 1139 print -- "$comm" 1140 return 1141 fi 1142 1143 print -- "$comm" | html_quote | bug2url | sac2url 1144 1145 } 1146 1147 # 1148 # getcomments {text|html} filepath parentpath 1149 # 1150 # Fetch the comments depending on what SCM mode we're in. 1151 # 1152 getcomments() 1153 { 1154 typeset fmt=$1 1155 typeset p=$2 1156 typeset pp=$3 1157 1158 if [[ -n $Nflag ]]; then 1159 return 1160 fi 1161 # 1162 # Mercurial support uses a file list in wx format, so this 1163 # will be used there, too 1164 # 1165 if [[ -n $wxfile ]]; then 1166 comments_from_wx $fmt $p 1167 else 1168 if [[ $SCM_MODE == "teamware" ]]; then 1169 comments_from_teamware $fmt $pp $p 1170 fi 1171 fi 1172 } 1173 1174 # 1175 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged> 1176 # 1177 # Print out Code Inspection figures similar to sccs-prt(1) format. 1178 # 1179 function printCI 1180 { 1181 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5 1182 typeset str 1183 if (( tot == 1 )); then 1184 str="line" 1185 else 1186 str="lines" 1187 fi 1188 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \ 1189 $tot $str $ins $del $mod $unc 1190 } 1191 1192 1193 # 1194 # difflines <oldfile> <newfile> 1195 # 1196 # Calculate and emit number of added, removed, modified and unchanged lines, 1197 # and total lines changed, the sum of added + removed + modified. 1198 # 1199 function difflines 1200 { 1201 integer tot mod del ins unc err 1202 typeset filename 1203 1204 eval $( diff -e $1 $2 | $AWK ' 1205 # Change range of lines: N,Nc 1206 /^[0-9]*,[0-9]*c$/ { 1207 n=split(substr($1,1,length($1)-1), counts, ","); 1208 if (n != 2) { 1209 error=2 1210 exit; 1211 } 1212 # 1213 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines. 1214 # following would be 5 - 3 = 2! Hence +1 for correction. 1215 # 1216 r=(counts[2]-counts[1])+1; 1217 1218 # 1219 # Now count replacement lines: each represents a change instead 1220 # of a delete, so increment c and decrement r. 1221 # 1222 while (getline != /^\.$/) { 1223 c++; 1224 r--; 1225 } 1226 # 1227 # If there were more replacement lines than original lines, 1228 # then r will be negative; in this case there are no deletions, 1229 # but there are r changes that should be counted as adds, and 1230 # since r is negative, subtract it from a and add it to c. 1231 # 1232 if (r < 0) { 1233 a-=r; 1234 c+=r; 1235 } 1236 1237 # 1238 # If there were more original lines than replacement lines, then 1239 # r will be positive; in this case, increment d by that much. 1240 # 1241 if (r > 0) { 1242 d+=r; 1243 } 1244 next; 1245 } 1246 1247 # Change lines: Nc 1248 /^[0-9].*c$/ { 1249 # The first line is a replacement; any more are additions. 1250 if (getline != /^\.$/) { 1251 c++; 1252 while (getline != /^\.$/) a++; 1253 } 1254 next; 1255 } 1256 1257 # Add lines: both Na and N,Na 1258 /^[0-9].*a$/ { 1259 while (getline != /^\.$/) a++; 1260 next; 1261 } 1262 1263 # Delete range of lines: N,Nd 1264 /^[0-9]*,[0-9]*d$/ { 1265 n=split(substr($1,1,length($1)-1), counts, ","); 1266 if (n != 2) { 1267 error=2 1268 exit; 1269 } 1270 # 1271 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines. 1272 # following would be 5 - 3 = 2! Hence +1 for correction. 1273 # 1274 r=(counts[2]-counts[1])+1; 1275 d+=r; 1276 next; 1277 } 1278 1279 # Delete line: Nd. For example 10d says line 10 is deleted. 1280 /^[0-9]*d$/ {d++; next} 1281 1282 # Should not get here! 1283 { 1284 error=1; 1285 exit; 1286 } 1287 1288 # Finish off - print results 1289 END { 1290 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n", 1291 (c+d+a), c, d, a, error); 1292 }' ) 1293 1294 # End of $AWK, Check to see if any trouble occurred. 1295 if (( $? > 0 || err > 0 )); then 1296 print "Unexpected Error occurred reading" \ 1297 "\`diff -e $1 $2\`: \$?=$?, err=" $err 1298 return 1299 fi 1300 1301 # Accumulate totals 1302 (( TOTL += tot )) 1303 (( TMOD += mod )) 1304 (( TDEL += del )) 1305 (( TINS += ins )) 1306 # Calculate unchanged lines 1307 unc=`wc -l < $1` 1308 if (( unc > 0 )); then 1309 (( unc -= del + mod )) 1310 (( TUNC += unc )) 1311 fi 1312 # print summary 1313 print "<span class=\"lineschanged\">" 1314 printCI $tot $ins $del $mod $unc 1315 print "</span>" 1316 } 1317 1318 1319 # 1320 # flist_from_wx 1321 # 1322 # Sets up webrev to source its information from a wx-formatted file. 1323 # Sets the global 'wxfile' variable. 1324 # 1325 function flist_from_wx 1326 { 1327 typeset argfile=$1 1328 if [[ -n ${argfile%%/*} ]]; then