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 38REMOVED_COLOR=brown 39CHANGED_COLOR=blue 40NEW_COLOR=blue 41 42HTML='<?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 47FRAMEHTML='<?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 52STDHEAD='<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"> 66body { 67 background-color: #eeeeee; 68} 69hr { 70 border: none 0; 71 border-top: 1px solid #aaa; 72 height: 1px; 73} 74div.summary { 75 font-size: .8em; 76 border-bottom: 1px solid #aaa; 77 padding-left: 1em; 78 padding-right: 1em; 79} 80div.summary h2 { 81 margin-bottom: 0.3em; 82} 83div.summary table th { 84 text-align: right; 85 vertical-align: top; 86 white-space: nowrap; 87} 88span.lineschanged { 89 font-size: 0.7em; 90} 91span.oldmarker { 92 color: red; 93 font-size: large; 94 font-weight: bold; 95} 96span.newmarker { 97 color: green; 98 font-size: large; 99 font-weight: bold; 100} 101span.removed { 102 color: brown; 103} 104span.changed { 105 color: blue; 106} 107span.new { 108 color: blue; 109 font-weight: bold; 110} 111span.chmod { 112 font-size: 0.7em; 113 color: #db7800; 114} 115a.print { font-size: x-small; } 116a:hover { background-color: #ffcc99; } 117</style> 118 119<style type="text/css" media="print"> 120pre { font-size: 0.8em; font-family: courier, monospace; } 121span.removed { color: #444; font-style: italic } 122span.changed { font-weight: bold; } 123span.new { font-weight: bold; } 124span.newmarker { font-size: 1.2em; font-weight: bold; } 125span.oldmarker { font-size: 1.2em; font-weight: bold; } 126a.print {display: none} 127hr { 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# 135UDIFFCSS=' 136<style type="text/css" media="screen"> 137span.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# 151html_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# 161bug2url() 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# 174sac2url() 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# 199strip_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# 294sdiff_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# 512function 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# 579function 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"; 623EOF 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# 645function 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# 696function 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# 727function frame_nav_js 728{ 729cat << \EOF 730var myInt; 731var scrolling=0; 732var sfactor = 3; 733var scount=10; 734 735function 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 745function 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 767function getAncValue() 768{ 769 return (parseInt(parent.nav.document.diff.real.value)); 770} 771 772function 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 800function stopScroll() { 801 if (scrolling==1) { 802 clearInterval(myInt); 803 scrolling=0; 804 } 805} 806 807function startScroll() { 808 stopScroll(); 809 scrolling=1; 810 myInt=setInterval("scrollByPix()",10); 811} 812 813function 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 839function handleRelease(b) { 840 stopScroll(); 841} 842 843function 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 865function 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 886EOF 887} 888 889# 890# frame_navigation 891# 892# Output anchor navigation file for framed sdiffs. 893# 894function 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> 910EOF 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> 986EOF 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# 997diff_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# 1049source_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# 1072comments_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# 1121comments_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# 1152getcomments() 1153{ 1154 typeset fmt=$1 1155 typeset p=$2 1156 typeset pp=$3 1157 1158 # 1159 # Mercurial support uses a file list in wx format, so this 1160 # will be used there, too 1161 # 1162 if [[ -n $wxfile ]]; then 1163 comments_from_wx $fmt $p 1164 else 1165 if [[ $SCM_MODE == "teamware" ]]; then 1166 comments_from_teamware $fmt $pp $p 1167 fi 1168 fi 1169} 1170 1171# 1172# printCI <total-changed> <inserted> <deleted> <modified> <unchanged> 1173# 1174# Print out Code Inspection figures similar to sccs-prt(1) format. 1175# 1176function printCI 1177{ 1178 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5 1179 typeset str 1180 if (( tot == 1 )); then 1181 str="line" 1182 else 1183 str="lines" 1184 fi 1185 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \ 1186 $tot $str $ins $del $mod $unc 1187} 1188 1189 1190# 1191# difflines <oldfile> <newfile> 1192# 1193# Calculate and emit number of added, removed, modified and unchanged lines, 1194# and total lines changed, the sum of added + removed + modified. 1195# 1196function difflines 1197{ 1198 integer tot mod del ins unc err 1199 typeset filename 1200 1201 eval $( diff -e $1 $2 | $AWK ' 1202 # Change range of lines: N,Nc 1203 /^[0-9]*,[0-9]*c$/ { 1204 n=split(substr($1,1,length($1)-1), counts, ","); 1205 if (n != 2) { 1206 error=2 1207 exit; 1208 } 1209 # 1210 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines. 1211 # following would be 5 - 3 = 2! Hence +1 for correction. 1212 # 1213 r=(counts[2]-counts[1])+1; 1214 1215 # 1216 # Now count replacement lines: each represents a change instead 1217 # of a delete, so increment c and decrement r. 1218 # 1219 while (getline != /^\.$/) { 1220 c++; 1221 r--; 1222 } 1223 # 1224 # If there were more replacement lines than original lines, 1225 # then r will be negative; in this case there are no deletions, 1226 # but there are r changes that should be counted as adds, and 1227 # since r is negative, subtract it from a and add it to c. 1228 # 1229 if (r < 0) { 1230 a-=r; 1231 c+=r; 1232 } 1233 1234 # 1235 # If there were more original lines than replacement lines, then 1236 # r will be positive; in this case, increment d by that much. 1237 # 1238 if (r > 0) { 1239 d+=r; 1240 } 1241 next; 1242 } 1243 1244 # Change lines: Nc 1245 /^[0-9].*c$/ { 1246 # The first line is a replacement; any more are additions. 1247 if (getline != /^\.$/) { 1248 c++; 1249 while (getline != /^\.$/) a++; 1250 } 1251 next; 1252 } 1253 1254 # Add lines: both Na and N,Na 1255 /^[0-9].*a$/ { 1256 while (getline != /^\.$/) a++; 1257 next; 1258 } 1259 1260 # Delete range of lines: N,Nd 1261 /^[0-9]*,[0-9]*d$/ { 1262 n=split(substr($1,1,length($1)-1), counts, ","); 1263 if (n != 2) { 1264 error=2 1265 exit; 1266 } 1267 # 1268 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines. 1269 # following would be 5 - 3 = 2! Hence +1 for correction. 1270 # 1271 r=(counts[2]-counts[1])+1; 1272 d+=r; 1273 next; 1274 } 1275 1276 # Delete line: Nd. For example 10d says line 10 is deleted. 1277 /^[0-9]*d$/ {d++; next} 1278 1279 # Should not get here! 1280 { 1281 error=1; 1282 exit; 1283 } 1284 1285 # Finish off - print results 1286 END { 1287 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n", 1288 (c+d+a), c, d, a, error); 1289 }' ) 1290 1291 # End of $AWK, Check to see if any trouble occurred. 1292 if (( $? > 0 || err > 0 )); then 1293 print "Unexpected Error occurred reading" \ 1294 "\`diff -e $1 $2\`: \$?=$?, err=" $err 1295 return 1296 fi 1297 1298 # Accumulate totals 1299 (( TOTL += tot )) 1300 (( TMOD += mod )) 1301 (( TDEL += del )) 1302 (( TINS += ins )) 1303 # Calculate unchanged lines 1304 unc=`wc -l < $1` 1305 if (( unc > 0 )); then 1306 (( unc -= del + mod )) 1307 (( TUNC += unc )) 1308 fi 1309 # print summary 1310 print "<span class=\"lineschanged\">" 1311 printCI $tot $ins $del $mod $unc 1312 print "</span>" 1313} 1314 1315 1316# 1317# flist_from_wx 1318# 1319# Sets up webrev to source its information from a wx-formatted file. 1320# Sets the global 'wxfile' variable. 1321# 1322function flist_from_wx 1323{ 1324 typeset argfile=$1 1325 if [[ -n ${argfile%%/*} ]]; then 1326 # 1327 # If the wx file pathname is relative then make it absolute 1328 # because the webrev does a "cd" later on. 1329 # 1330 wxfile=$PWD/$argfile 1331 else 1332 wxfile=$argfile 1333 fi 1334 1335 $AWK '{ c = 1; print; 1336 while (getline) { 1337 if (NF == 0) { c = -c; continue } 1338 if (c > 0) print 1339 } 1340 }' $wxfile > $FLIST 1341 1342 print " Done." 1343} 1344 1345# 1346# flist_from_teamware [ <args-to-putback-n> ] 1347# 1348# Generate the file list by extracting file names from a putback -n. Some 1349# names may come from the "update/create" messages and others from the 1350# "currently checked out" warning. Renames are detected here too. Extract 1351# values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback 1352# -n as well, but remove them if they are already defined. 1353# 1354function flist_from_teamware 1355{ 1356 if [[ -n $codemgr_parent && -z $parent_webrev ]]; then 1357 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then 1358 print -u2 "parent $codemgr_parent doesn't look like a" \ 1359 "valid teamware workspace" 1360 exit 1 1361 fi 1362 parent_args="-p $codemgr_parent" 1363 fi 1364 1365 print " File list from: 'putback -n $parent_args $*' ... \c" 1366 1367 putback -n $parent_args $* 2>&1 | 1368 $AWK ' 1369 /^update:|^create:/ {print $2} 1370 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)} 1371 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)} 1372 /^The following files are currently checked out/ {p = 1; continue} 1373 NF == 0 {p=0 ; continue} 1374 /^rename/ {old=$3} 1375 $1 == "to:" {print $2, old} 1376 /^"/ {continue} 1377 p == 1 {print $1}' | 1378 sort -r -k 1,1 -u | sort > $FLIST 1379 1380 print " Done." 1381} 1382 1383# 1384# Call hg-active to get the active list output in the wx active list format 1385# 1386function hg_active_wxfile 1387{ 1388 typeset child=$1 1389 typeset parent=$2 1390 1391 TMPFLIST=/tmp/$$.active 1392 $HG_ACTIVE -w $child -p $parent -o $TMPFLIST 1393 wxfile=$TMPFLIST 1394} 1395 1396# 1397# flist_from_mercurial 1398# Call hg-active to get a wx-style active list, and hand it off to 1399# flist_from_wx 1400# 1401function flist_from_mercurial 1402{ 1403 typeset child=$1 1404 typeset parent=$2 1405 1406 print " File list from: hg-active -p $parent ...\c" 1407 1408 if [[ ! -x $HG_ACTIVE ]]; then 1409 print # Blank line for the \c above 1410 print -u2 "Error: hg-active tool not found. Exiting" 1411 exit 1 1412 fi 1413 hg_active_wxfile $child $parent 1414 1415 # flist_from_wx prints the Done, so we don't have to. 1416 flist_from_wx $TMPFLIST 1417} 1418 1419# 1420# flist_from_subversion 1421# 1422# Generate the file list by extracting file names from svn status. 1423# 1424function flist_from_subversion 1425{ 1426 CWS=$1 1427 OLDPWD=$2 1428 1429 cd $CWS 1430 print -u2 " File list from: svn status ... \c" 1431 svn status | $AWK '/^[ACDMR]/ { print $NF }' > $FLIST 1432 print -u2 " Done." 1433 cd $OLDPWD 1434} 1435 1436function env_from_flist 1437{ 1438 [[ -r $FLIST ]] || return 1439 1440 # 1441 # Use "eval" to set env variables that are listed in the file 1442 # list. Then copy those into our local versions of those 1443 # variables if they have not been set already. 1444 # 1445 eval `sed -e "s/#.*$//" $FLIST | grep = ` 1446 1447 if [[ -z $codemgr_ws && -n $CODEMGR_WS ]]; then 1448 codemgr_ws=$CODEMGR_WS 1449 export CODEMGR_WS 1450 fi 1451 1452 # 1453 # Check to see if CODEMGR_PARENT is set in the flist file. 1454 # 1455 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then 1456 codemgr_parent=$CODEMGR_PARENT 1457 export CODEMGR_PARENT 1458 fi 1459} 1460 1461function look_for_prog 1462{ 1463 typeset path 1464 typeset ppath 1465 typeset progname=$1 1466 1467 ppath=$PATH 1468 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin 1469 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin 1470 ppath=$ppath:/opt/onbld/bin/`uname -p` 1471 1472 PATH=$ppath prog=`whence $progname` 1473 if [[ -n $prog ]]; then 1474 print $prog 1475 fi 1476} 1477 1478function get_file_mode 1479{ 1480 $PERL -e ' 1481 if (@stat = stat($ARGV[0])) { 1482 $mode = $stat[2] & 0777; 1483 printf "%03o\n", $mode; 1484 exit 0; 1485 } else { 1486 exit 1; 1487 } 1488 ' $1 1489} 1490 1491function build_old_new_teamware 1492{ 1493 typeset olddir="$1" 1494 typeset newdir="$2" 1495 1496 # If the child's version doesn't exist then 1497 # get a readonly copy. 1498 1499 if [[ ! -f $CWS/$DIR/$F && -f $CWS/$DIR/SCCS/s.$F ]]; then 1500 $SCCS get -s -p $CWS/$DIR/$F > $CWS/$DIR/$F 1501 fi 1502 1503 # The following two sections propagate file permissions the 1504 # same way SCCS does. If the file is already under version 1505 # control, always use permissions from the SCCS/s.file. If 1506 # the file is not under SCCS control, use permissions from the 1507 # working copy. In all cases, the file copied to the webrev 1508 # is set to read only, and group/other permissions are set to 1509 # match those of the file owner. This way, even if the file 1510 # is currently checked out, the webrev will display the final 1511 # permissions that would result after check in. 1512 1513 # 1514 # Snag new version of file. 1515 # 1516 rm -f $newdir/$DIR/$F 1517 cp $CWS/$DIR/$F $newdir/$DIR/$F 1518 if [[ -f $CWS/$DIR/SCCS/s.$F ]]; then 1519 chmod `get_file_mode $CWS/$DIR/SCCS/s.$F` \ 1520 $newdir/$DIR/$F 1521 fi 1522 chmod u-w,go=u $newdir/$DIR/$F 1523 1524 # 1525 # Get the parent's version of the file. First see whether the 1526 # child's version is checked out and get the parent's version 1527 # with keywords expanded or unexpanded as appropriate. 1528 # 1529 if [[ -f $PWS/$PDIR/$PF && ! -f $PWS/$PDIR/SCCS/s.$PF && \ 1530 ! -f $PWS/$PDIR/SCCS/p.$PF ]]; then 1531 # Parent is not a real workspace, but just a raw 1532 # directory tree - use the file that's there as 1533 # the old file. 1534 1535 rm -f $olddir/$PDIR/$PF 1536 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1537 else 1538 if [[ -f $PWS/$PDIR/SCCS/s.$PF ]]; then 1539 real_parent=$PWS 1540 else 1541 real_parent=$RWS 1542 fi 1543 1544 rm -f $olddir/$PDIR/$PF 1545 1546 if [[ -f $real_parent/$PDIR/$PF ]]; then 1547 if [ -f $CWS/$DIR/SCCS/p.$F ]; then 1548 $SCCS get -s -p -k $real_parent/$PDIR/$PF > \ 1549 $olddir/$PDIR/$PF 1550 else 1551 $SCCS get -s -p $real_parent/$PDIR/$PF > \ 1552 $olddir/$PDIR/$PF 1553 fi 1554 chmod `get_file_mode $real_parent/$PDIR/SCCS/s.$PF` \ 1555 $olddir/$PDIR/$PF 1556 fi 1557 fi 1558 if [[ -f $olddir/$PDIR/$PF ]]; then 1559 chmod u-w,go=u $olddir/$PDIR/$PF 1560 fi 1561} 1562 1563function build_old_new_mercurial 1564{ 1565 typeset olddir="$1" 1566 typeset newdir="$2" 1567 typeset old_mode= 1568 typeset new_mode= 1569 typeset file 1570 1571 # 1572 # Get old file mode, from the parent revision manifest entry. 1573 # Mercurial only stores a "file is executable" flag, but the 1574 # manifest will display an octal mode "644" or "755". 1575 # 1576 if [[ "$PDIR" == "." ]]; then 1577 file="$PF" 1578 else 1579 file="$PDIR/$PF" 1580 fi 1581 file=`echo $file | sed 's#/#\\\/#g'` 1582 # match the exact filename, and return only the permission digits 1583 old_mode=`sed -n -e "/^\\(...\\) . ${file}$/s//\\1/p" \ 1584 < $HG_PARENT_MANIFEST` 1585 1586 # 1587 # Get new file mode, directly from the filesystem. 1588 # Normalize the mode to match Mercurial's behavior. 1589 # 1590 new_mode=`get_file_mode $CWS/$DIR/$F` 1591 if [[ -n "$new_mode" ]]; then 1592 if [[ "$new_mode" = *[1357]* ]]; then 1593 new_mode=755 1594 else 1595 new_mode=644 1596 fi 1597 fi 1598 1599 # 1600 # new version of the file. 1601 # 1602 rm -rf $newdir/$DIR/$F 1603 if [[ -e $CWS/$DIR/$F ]]; then 1604 cp $CWS/$DIR/$F $newdir/$DIR/$F 1605 if [[ -n $new_mode ]]; then 1606 chmod $new_mode $newdir/$DIR/$F 1607 else 1608 # should never happen 1609 print -u2 "ERROR: set mode of $newdir/$DIR/$F" 1610 fi 1611 fi 1612 1613 # 1614 # parent's version of the file 1615 # 1616 # Note that we get this from the last version common to both 1617 # ourselves and the parent. References are via $CWS since we have no 1618 # guarantee that the parent workspace is reachable via the filesystem. 1619 # 1620 if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then 1621 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1622 elif [[ -n $HG_PARENT ]]; then 1623 hg cat -R $CWS -r $HG_PARENT $CWS/$PDIR/$PF > \ 1624 $olddir/$PDIR/$PF 2>/dev/null 1625 1626 if [ $? -ne 0 ]; then 1627 rm -f $olddir/$PDIR/$PF 1628 else 1629 if [[ -n $old_mode ]]; then 1630 chmod $old_mode $olddir/$PDIR/$PF 1631 else 1632 # should never happen 1633 print -u2 "ERROR: set mode of $olddir/$PDIR/$PF" 1634 fi 1635 fi 1636 fi 1637} 1638 1639function build_old_new_subversion 1640{ 1641 typeset olddir="$1" 1642 typeset newdir="$2" 1643 1644 # Snag new version of file. 1645 rm -f $newdir/$DIR/$F 1646 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F 1647 1648 if [[ -n $PWS && -e $PWS/$PDIR/$PF ]]; then 1649 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1650 else 1651 # Get the parent's version of the file. 1652 svn status $CWS/$DIR/$F | read stat file 1653 if [[ $stat != "A" ]]; then 1654 svn cat -r BASE $CWS/$DIR/$F > $olddir/$PDIR/$PF 1655 fi 1656 fi 1657} 1658 1659function build_old_new_unknown 1660{ 1661 typeset olddir="$1" 1662 typeset newdir="$2" 1663 1664 # 1665 # Snag new version of file. 1666 # 1667 rm -f $newdir/$DIR/$F 1668 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F 1669 1670 # 1671 # Snag the parent's version of the file. 1672 # 1673 if [[ -f $PWS/$PDIR/$PF ]]; then 1674 rm -f $olddir/$PDIR/$PF 1675 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1676 fi 1677} 1678 1679function build_old_new 1680{ 1681 typeset WDIR=$1 1682 typeset PWS=$2 1683 typeset PDIR=$3 1684 typeset PF=$4 1685 typeset CWS=$5 1686 typeset DIR=$6 1687 typeset F=$7 1688 1689 typeset olddir="$WDIR/raw_files/old" 1690 typeset newdir="$WDIR/raw_files/new" 1691 1692 mkdir -p $olddir/$PDIR 1693 mkdir -p $newdir/$DIR 1694 1695 if [[ $SCM_MODE == "teamware" ]]; then 1696 build_old_new_teamware "$olddir" "$newdir" 1697 elif [[ $SCM_MODE == "mercurial" ]]; then 1698 build_old_new_mercurial "$olddir" "$newdir" 1699 elif [[ $SCM_MODE == "subversion" ]]; then 1700 build_old_new_subversion "$olddir" "$newdir" 1701 elif [[ $SCM_MODE == "unknown" ]]; then 1702 build_old_new_unknown "$olddir" "$newdir" 1703 fi 1704 1705 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then 1706 print "*** Error: file not in parent or child" 1707 return 1 1708 fi 1709 return 0 1710} 1711 1712 1713# 1714# Usage message. 1715# 1716function usage 1717{ 1718 print 'Usage:\twebrev [common-options] 1719 webrev [common-options] ( <file> | - ) 1720 webrev [common-options] -w <wx file> 1721 1722Options: 1723 -O: Print bugids/arc cases suitable for OpenSolaris. 1724 -i <filename>: Include <filename> in the index.html file. 1725 -o <outdir>: Output webrev to specified directory. 1726 -p <compare-against>: Use specified parent wkspc or basis for comparison 1727 -w <wxfile>: Use specified wx active file. 1728 1729Environment: 1730 WDIR: Control the output directory. 1731 WEBREV_BUGURL: Control the URL prefix for bugids. 1732 WEBREV_SACURL: Control the URL prefix for ARC cases. 1733 1734SCM Specific Options: 1735 TeamWare: webrev [common-options] -l [arguments to 'putback'] 1736 1737SCM Environment: 1738 CODEMGR_WS: Workspace location. 1739 CODEMGR_PARENT: Parent workspace location. 1740' 1741 1742 exit 2 1743} 1744 1745# 1746# 1747# Main program starts here 1748# 1749# 1750 1751trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15 1752 1753set +o noclobber 1754 1755PATH=$(dirname $(whence $0)):$PATH 1756 1757[[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` 1758[[ -z $WX ]] && WX=`look_for_prog wx` 1759[[ -z $HG_ACTIVE ]] && HG_ACTIVE=`look_for_prog hg-active` 1760[[ -z $WHICH_SCM ]] && WHICH_SCM=`look_for_prog which_scm` 1761[[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` 1762[[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf` 1763[[ -z $PERL ]] && PERL=`look_for_prog perl` 1764[[ -z $SCCS ]] && SCCS=`look_for_prog sccs` 1765[[ -z $AWK ]] && AWK=`look_for_prog nawk` 1766[[ -z $AWK ]] && AWK=`look_for_prog gawk` 1767[[ -z $AWK ]] && AWK=`look_for_prog awk` 1768 1769 1770if [[ ! -x $PERL ]]; then 1771 print -u2 "Error: No perl interpreter found. Exiting." 1772 exit 1 1773fi 1774 1775if [[ ! -x $WHICH_SCM ]]; then 1776 print -u2 "Error: Could not find which_scm. Exiting." 1777 exit 1 1778fi 1779 1780# 1781# These aren't fatal, but we want to note them to the user. 1782# We don't warn on the absence of 'wx' until later when we've 1783# determined that we actually need to try to invoke it. 1784# 1785[[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found." 1786[[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found." 1787[[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found." 1788 1789# Declare global total counters. 1790integer TOTL TINS TDEL TMOD TUNC 1791 1792flist_mode= 1793flist_file= 1794iflag= 1795oflag= 1796pflag= 1797lflag= 1798wflag= 1799Oflag= 1800while getopts "i:o:p:lwO" opt 1801do 1802 case $opt in 1803 i) iflag=1 1804 INCLUDE_FILE=$OPTARG;; 1805 1806 o) oflag=1 1807 WDIR=$OPTARG;; 1808 1809 p) pflag=1 1810 codemgr_parent=$OPTARG;; 1811 1812 # 1813 # If -l has been specified, we need to abort further options 1814 # processing, because subsequent arguments are going to be 1815 # arguments to 'putback -n'. 1816 # 1817 l) lflag=1 1818 break;; 1819 1820 w) wflag=1;; 1821 1822 O) Oflag=1;; 1823 1824 ?) usage;; 1825 esac 1826done 1827 1828FLIST=/tmp/$$.flist 1829 1830if [[ -n $wflag && -n $lflag ]]; then 1831 usage 1832fi 1833 1834# 1835# If this manually set as the parent, and it appears to be an earlier webrev, 1836# then note that fact and set the parent to the raw_files/new subdirectory. 1837# 1838if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then 1839 parent_webrev="$codemgr_parent" 1840 codemgr_parent="$codemgr_parent/raw_files/new" 1841fi 1842 1843if [[ -z $wflag && -z $lflag ]]; then 1844 shift $(($OPTIND - 1)) 1845 1846 if [[ $1 == "-" ]]; then 1847 cat > $FLIST 1848 flist_mode="stdin" 1849 flist_done=1 1850 shift 1851 elif [[ -n $1 ]]; then 1852 if [[ ! -r $1 ]]; then 1853 print -u2 "$1: no such file or not readable" 1854 usage 1855 fi 1856 cat $1 > $FLIST 1857 flist_mode="file" 1858 flist_file=$1 1859 flist_done=1 1860 shift 1861 else 1862 flist_mode="auto" 1863 fi 1864fi 1865 1866# 1867# Before we go on to further consider -l and -w, work out which SCM we think 1868# is in use. 1869# 1870$WHICH_SCM | read SCM_MODE junk || exit 1 1871case "$SCM_MODE" in 1872teamware|mercurial|subversion) 1873 ;; 1874unknown) 1875 if [[ $flist_mode == "auto" ]]; then 1876 print -u2 "Unable to determine SCM in use and file list not specified" 1877 print -u2 "See which_scm(1) for SCM detection information." 1878 exit 1 1879 fi 1880 ;; 1881*) 1882 if [[ $flist_mode == "auto" ]]; then 1883 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified" 1884 exit 1 1885 fi 1886 ;; 1887esac 1888 1889print -u2 " SCM detected: $SCM_MODE" 1890 1891if [[ -n $lflag ]]; then 1892 # 1893 # If the -l flag is given instead of the name of a file list, 1894 # then generate the file list by extracting file names from a 1895 # putback -n. 1896 # 1897 shift $(($OPTIND - 1)) 1898 if [[ $SCM_MODE == "teamware" ]]; then 1899 flist_from_teamware "$*" 1900 else 1901 print -u2 -- "Error: -l option only applies to TeamWare" 1902 exit 1 1903 fi 1904 flist_done=1 1905 shift $# 1906elif [[ -n $wflag ]]; then 1907 # 1908 # If the -w is given then assume the file list is in Bonwick's "wx" 1909 # command format, i.e. pathname lines alternating with SCCS comment 1910 # lines with blank lines as separators. Use the SCCS comments later 1911 # in building the index.html file. 1912 # 1913 shift $(($OPTIND - 1)) 1914 wxfile=$1 1915 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then 1916 if [[ -r $CODEMGR_WS/wx/active ]]; then 1917 wxfile=$CODEMGR_WS/wx/active 1918 fi 1919 fi 1920 1921 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \ 1922 "be auto-detected (check \$CODEMGR_WS)" && exit 1 1923 1924 if [[ ! -r $wxfile ]]; then 1925 print -u2 "$wxfile: no such file or not readable" 1926 usage 1927 fi 1928 1929 print -u2 " File list from: wx 'active' file '$wxfile' ... \c" 1930 flist_from_wx $wxfile 1931 flist_done=1 1932 if [[ -n "$*" ]]; then 1933 shift 1934 fi 1935elif [[ $flist_mode == "stdin" ]]; then 1936 print -u2 " File list from: standard input" 1937elif [[ $flist_mode == "file" ]]; then 1938 print -u2 " File list from: $flist_file" 1939fi 1940 1941if [[ $# -gt 0 ]]; then 1942 print -u2 "WARNING: unused arguments: $*" 1943fi 1944 1945if [[ $SCM_MODE == "teamware" ]]; then 1946 # 1947 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can 1948 # be set in a number of ways, in decreasing precedence: 1949 # 1950 # 1) on the command line (only for the parent) 1951 # 2) in the user environment 1952 # 3) in the flist 1953 # 4) automatically based on the workspace (only for the parent) 1954 # 1955 1956 # 1957 # Here is case (2): the user environment 1958 # 1959 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1960 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then 1961 print -u2 "$codemgr_ws: no such workspace" 1962 exit 1 1963 fi 1964 1965 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 1966 codemgr_parent=$CODEMGR_PARENT 1967 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then 1968 print -u2 "$codemgr_parent: no such directory" 1969 exit 1 1970 fi 1971 1972 # 1973 # If we're in auto-detect mode and we haven't already gotten the file 1974 # list, then see if we can get it by probing for wx. 1975 # 1976 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then 1977 if [[ ! -x $WX ]]; then 1978 print -u2 "WARNING: wx not found!" 1979 fi 1980 1981 # 1982 # We need to use wx list -w so that we get renamed files, etc. 1983 # but only if a wx active file exists-- otherwise wx will 1984 # hang asking us to initialize our wx information. 1985 # 1986 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then 1987 print -u2 " File list from: 'wx list -w' ... \c" 1988 $WX list -w > $FLIST 1989 $WX comments > /tmp/$$.wx_comments 1990 wxfile=/tmp/$$.wx_comments 1991 print -u2 "done" 1992 flist_done=1 1993 fi 1994 fi 1995 1996 # 1997 # If by hook or by crook we've gotten a file list by now (perhaps 1998 # from the command line), eval it to extract environment variables from 1999 # it: This is step (3). 2000 # 2001 env_from_flist 2002 2003 # 2004 # Continuing step (3): If we still have no file list, we'll try to get 2005 # it from teamware. 2006 # 2007 if [[ -z $flist_done ]]; then 2008 flist_from_teamware 2009 env_from_flist 2010 fi 2011 2012 # 2013 # (4) If we still don't have a value for codemgr_parent, get it 2014 # from workspace. 2015 # 2016 [[ -z $codemgr_ws ]] && codemgr_ws=`workspace name` 2017 [[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent` 2018 if [[ ! -d $codemgr_parent ]]; then 2019 print -u2 "$CODEMGR_PARENT: no such parent workspace" 2020 exit 1 2021 fi 2022 2023 # 2024 # Observe true directory name of CODEMGR_WS, as used later in 2025 # webrev title. 2026 # 2027 codemgr_ws=$(cd $codemgr_ws;print $PWD) 2028 2029 # 2030 # Reset CODEMGR_WS to make sure teamware commands are happy. 2031 # 2032 CODEMGR_WS=$codemgr_ws 2033 CWS=$codemgr_ws 2034 PWS=$codemgr_parent 2035 2036 [[ -n $parent_webrev ]] && RWS=$(workspace parent $CWS) 2037 2038elif [[ $SCM_MODE == "mercurial" ]]; then 2039 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && \ 2040 codemgr_ws=`hg root -R $CODEMGR_WS 2>/dev/null` 2041 2042 [[ -z $codemgr_ws ]] && codemgr_ws=`hg root 2>/dev/null` 2043 2044 # 2045 # Parent can either be specified with -p 2046 # Specified with CODEMGR_PARENT in the environment 2047 # or taken from hg's default path. 2048 # 2049 2050 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then 2051 codemgr_parent=$CODEMGR_PARENT 2052 fi 2053 2054 if [[ -z $codemgr_parent ]]; then 2055 codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null` 2056 fi 2057 2058 CWS_REV=`hg parent -R $codemgr_ws --template '{node|short}' 2>/dev/null` 2059 CWS=$codemgr_ws 2060 PWS=$codemgr_parent 2061 2062 # 2063 # If the parent is a webrev, we want to do some things against 2064 # the natural workspace parent (file list, comments, etc) 2065 # 2066 if [[ -n $parent_webrev ]]; then 2067 real_parent=$(hg path -R $codemgr_ws default 2>/dev/null) 2068 else 2069 real_parent=$PWS 2070 fi 2071 2072 # 2073 # If hg-active exists, then we run it. In the case of no explicit 2074 # flist given, we'll use it for our comments. In the case of an 2075 # explicit flist given we'll try to use it for comments for any 2076 # files mentioned in the flist. 2077 # 2078 if [[ -z $flist_done ]]; then 2079 flist_from_mercurial $CWS $real_parent 2080 flist_done=1 2081 fi 2082 2083 # 2084 # If we have a file list now, pull out any variables set 2085 # therein. We do this now (rather than when we possibly use 2086 # hg-active to find comments) to avoid stomping specifications 2087 # in the user-specified flist. 2088 # 2089 if [[ -n $flist_done ]]; then 2090 env_from_flist 2091 fi 2092 2093 # 2094 # Only call hg-active if we don't have a wx formatted file already 2095 # 2096 if [[ -x $HG_ACTIVE && -z $wxfile ]]; then 2097 print " Comments from: hg-active -p $real_parent ...\c" 2098 hg_active_wxfile $CWS $real_parent 2099 print " Done." 2100 fi 2101 2102 # 2103 # At this point we must have a wx flist either from hg-active, 2104 # or in general. Use it to try and find our parent revision, 2105 # if we don't have one. 2106 # 2107 if [[ -z $HG_PARENT ]]; then 2108 eval `sed -e "s/#.*$//" $wxfile | grep HG_PARENT=` 2109 fi 2110 2111 # 2112 # If we still don't have a parent, we must have been given a 2113 # wx-style active list with no HG_PARENT specification, run 2114 # hg-active and pull an HG_PARENT out of it, ignore the rest. 2115 # 2116 if [[ -z $HG_PARENT && -x $HG_ACTIVE ]]; then 2117 $HG_ACTIVE -w $codemgr_ws -p $real_parent | \ 2118 eval `sed -e "s/#.*$//" | grep HG_PARENT=` 2119 elif [[ -z $HG_PARENT ]]; then 2120 print -u2 "Error: Cannot discover parent revision" 2121 exit 1 2122 fi 2123elif [[ $SCM_MODE == "subversion" ]]; then 2124 if [[ -n $CODEMGR_WS && -d $CODEMGR_WS/.svn ]]; then 2125 CWS=$CODEMGR_WS 2126 else 2127 svn info | while read line; do 2128 if [[ $line == "URL: "* ]]; then 2129 url=${line#URL: } 2130 elif [[ $line == "Repository Root: "* ]]; then 2131 repo=${line#Repository Root: } 2132 fi 2133 done 2134 2135 rel=${url#$repo} 2136 CWS=${PWD%$rel} 2137 fi 2138 2139 # 2140 # We only will have a real parent workspace in the case one 2141 # was specified (be it an older webrev, or another checkout). 2142 # 2143 [[ -n $codemgr_parent ]] && PWS=$codemgr_parent 2144 2145 if [[ -z $flist_done && $flist_mode == "auto" ]]; then 2146 flist_from_subversion $CWS $OLDPWD 2147 fi 2148else 2149 if [[ $SCM_MODE == "unknown" ]]; then 2150 print -u2 " Unknown type of SCM in use" 2151 else 2152 print -u2 " Unsupported SCM in use: $SCM_MODE" 2153 fi 2154 2155 env_from_flist 2156 2157 if [[ -z $CODEMGR_WS ]]; then 2158 print -u2 "SCM not detected/supported and CODEMGR_WS not specified" 2159 exit 1 2160 fi 2161 2162 if [[ -z $CODEMGR_PARENT ]]; then 2163 print -u2 "SCM not detected/supported and CODEMGR_PARENT not specified" 2164 exit 1 2165 fi 2166 2167 CWS=$CODEMGR_WS 2168 PWS=$CODEMGR_PARENT 2169fi 2170 2171# 2172# If the user didn't specify a -i option, check to see if there is a 2173# webrev-info file in the workspace directory. 2174# 2175if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then 2176 iflag=1 2177 INCLUDE_FILE="$CWS/webrev-info" 2178fi 2179 2180if [[ -n $iflag ]]; then 2181 if [[ ! -r $INCLUDE_FILE ]]; then 2182 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \ 2183 "not readable." 2184 exit 1 2185 else 2186 # 2187 # $INCLUDE_FILE may be a relative path, and the script alters 2188 # PWD, so we just stash a copy in /tmp. 2189 # 2190 cp $INCLUDE_FILE /tmp/$$.include 2191 fi 2192fi 2193 2194# 2195# Output directory. 2196# 2197WDIR=${WDIR:-$CWS/webrev} 2198 2199# 2200# Name of the webrev, derived from the workspace name; in the 2201# future this could potentially be an option. 2202# 2203WNAME=${CWS##*/} 2204 2205if [ "${WDIR%%/*}" ]; then 2206 WDIR=$PWD/$WDIR 2207fi 2208 2209if [[ ! -d $WDIR ]]; then 2210 mkdir -p $WDIR 2211 [[ $? != 0 ]] && exit 1 2212fi 2213 2214# 2215# Summarize what we're going to do. 2216# 2217if [[ -n $CWS_REV ]]; then 2218 print " Workspace: $CWS (at $CWS_REV)" 2219else 2220 print " Workspace: $CWS" 2221fi 2222if [[ -n $parent_webrev ]]; then 2223 print "Compare against: webrev at $parent_webrev" 2224else 2225 if [[ -n $HG_PARENT ]]; then 2226 hg_parent_short=`echo $HG_PARENT \ 2227 | sed -e 's/\([0-9a-f]\{12\}\).*/\1/'` 2228 print "Compare against: $PWS (at $hg_parent_short)" 2229 else 2230 print "Compare against: $PWS" 2231 fi 2232fi 2233 2234[[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE" 2235print " Output to: $WDIR" 2236 2237# 2238# Save the file list in the webrev dir 2239# 2240[[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list 2241 2242# 2243# Bug IDs will be replaced by a URL. Order of precedence 2244# is: default location, $WEBREV_BUGURL, the -O flag. 2245# 2246BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr=' 2247[[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL" 2248[[ -n "$Oflag" ]] && \ 2249 BUGURL='http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=' 2250 2251# 2252# Likewise, ARC cases will be replaced by a URL. Order of precedence 2253# is: default, $WEBREV_SACURL, the -O flag. 2254# 2255# Note that -O also triggers different substitution behavior for 2256# SACURL. See sac2url(). 2257# 2258SACURL='http://sac.eng.sun.com' 2259[[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL" 2260[[ -n "$Oflag" ]] && \ 2261 SACURL='http://www.opensolaris.org/os/community/arc/caselog' 2262 2263rm -f $WDIR/$WNAME.patch 2264rm -f $WDIR/$WNAME.ps 2265rm -f $WDIR/$WNAME.pdf 2266 2267touch $WDIR/$WNAME.patch 2268 2269print " Output Files:" 2270 2271# 2272# Clean up the file list: Remove comments, blank lines and env variables. 2273# 2274sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean 2275FLIST=/tmp/$$.flist.clean 2276 2277# 2278# For Mercurial, create a cache of manifest entries. 2279# 2280if [[ $SCM_MODE == "mercurial" ]]; then 2281 # 2282 # Transform the FLIST into a temporary sed script that matches 2283 # relevant entries in the Mercurial manifest as follows: 2284 # 1) The script will be used against the parent revision manifest, 2285 # so for FLIST lines that have two filenames (a renamed file) 2286 # keep only the old name. 2287 # 2) Escape all forward slashes the filename. 2288 # 3) Change the filename into another sed command that matches 2289 # that file in "hg manifest -v" output: start of line, three 2290 # octal digits for file permissions, space, a file type flag 2291 # character, space, the filename, end of line. 2292 # 2293 SEDFILE=/tmp/$$.manifest.sed 2294 sed ' 2295 s#^[^ ]* ## 2296 s#/#\\\/#g 2297 s#^.*$#/^... . &$/p# 2298 ' < $FLIST > $SEDFILE 2299 2300 # 2301 # Apply the generated script to the output of "hg manifest -v" 2302 # to get the relevant subset for this webrev. 2303 # 2304 HG_PARENT_MANIFEST=/tmp/$$.manifest 2305 hg -R $CWS manifest -v -r $HG_PARENT | 2306 sed -n -f $SEDFILE > $HG_PARENT_MANIFEST 2307fi 2308 2309# 2310# First pass through the files: generate the per-file webrev HTML-files. 2311# 2312cat $FLIST | while read LINE 2313do 2314 set - $LINE 2315 P=$1 2316 2317 # 2318 # Normally, each line in the file list is just a pathname of a 2319 # file that has been modified or created in the child. A file 2320 # that is renamed in the child workspace has two names on the 2321 # line: new name followed by the old name. 2322 # 2323 oldname="" 2324 oldpath="" 2325 rename= 2326 if [[ $# -eq 2 ]]; then 2327 PP=$2 # old filename 2328 oldname=" (was $PP)" 2329 oldpath="$PP" 2330 rename=1 2331 PDIR=${PP%/*} 2332 if [[ $PDIR == $PP ]]; then 2333 PDIR="." # File at root of workspace 2334 fi 2335 2336 PF=${PP##*/} 2337 2338 DIR=${P%/*} 2339 if [[ $DIR == $P ]]; then 2340 DIR="." # File at root of workspace 2341 fi 2342 2343 F=${P##*/} 2344 2345 else 2346 DIR=${P%/*} 2347 if [[ "$DIR" == "$P" ]]; then 2348 DIR="." # File at root of workspace 2349 fi 2350 2351 F=${P##*/} 2352 2353 PP=$P 2354 PDIR=$DIR 2355 PF=$F 2356 fi 2357 2358 COMM=`getcomments html $P $PP` 2359 2360 print "\t$P$oldname\n\t\t\c" 2361 2362 # Make the webrev mirror directory if necessary 2363 mkdir -p $WDIR/$DIR 2364 2365 # 2366 # If we're in OpenSolaris mode, we enforce a minor policy: 2367 # help to make sure the reviewer doesn't accidentally publish 2368 # source which is in usr/closed/* or deleted_files/usr/closed/* 2369 # 2370 if [[ -n "$Oflag" ]]; then 2371 pclosed=${P##usr/closed/} 2372 pdeleted=${P##deleted_files/usr/closed/} 2373 if [[ "$pclosed" != "$P" || "$pdeleted" != "$P" ]]; then 2374 print "*** Omitting closed source for OpenSolaris" \ 2375 "mode review" 2376 continue 2377 fi 2378 fi 2379 2380 # 2381 # We stash old and new files into parallel directories in $WDIR 2382 # and do our diffs there. This makes it possible to generate 2383 # clean looking diffs which don't have absolute paths present. 2384 # 2385 2386 build_old_new "$WDIR" "$PWS" "$PDIR" "$PF" "$CWS" "$DIR" "$F" || \ 2387 continue 2388 2389 # 2390 # Keep the old PWD around, so we can safely switch back after 2391 # diff generation, such that build_old_new runs in a 2392 # consistent environment. 2393 # 2394 OWD=$PWD 2395 cd $WDIR/raw_files 2396 ofile=old/$PDIR/$PF 2397 nfile=new/$DIR/$F 2398 2399 mv_but_nodiff= 2400 cmp $ofile $nfile > /dev/null 2>&1 2401 if [[ $? == 0 && $rename == 1 ]]; then 2402 mv_but_nodiff=1 2403 fi 2404 2405 # 2406 # If we have old and new versions of the file then run the appropriate 2407 # diffs. This is complicated by a couple of factors: 2408 # 2409 # - renames must be handled specially: we emit a 'remove' 2410 # diff and an 'add' diff 2411 # - new files and deleted files must be handled specially 2412 # - Solaris patch(1m) can't cope with file creation 2413 # (and hence renames) as of this writing. 2414 # - To make matters worse, gnu patch doesn't interpret the 2415 # output of Solaris diff properly when it comes to 2416 # adds and deletes. We need to do some "cleansing" 2417 # transformations: 2418 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ 2419 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ 2420 # 2421 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" 2422 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" 2423 2424 rm -f $WDIR/$DIR/$F.patch 2425 if [[ -z $rename ]]; then 2426 if [ ! -f "$ofile" ]; then 2427 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2428 > $WDIR/$DIR/$F.patch 2429 elif [ ! -f "$nfile" ]; then 2430 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2431 > $WDIR/$DIR/$F.patch 2432 else 2433 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch 2434 fi 2435 else 2436 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2437 > $WDIR/$DIR/$F.patch 2438 2439 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2440 >> $WDIR/$DIR/$F.patch 2441 2442 fi 2443 2444 # 2445 # Tack the patch we just made onto the accumulated patch for the 2446 # whole wad. 2447 # 2448 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch 2449 2450 print " patch\c" 2451 2452 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2453 2454 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff 2455 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2456 > $WDIR/$DIR/$F.cdiff.html 2457 print " cdiffs\c" 2458 2459 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff 2460 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2461 > $WDIR/$DIR/$F.udiff.html 2462 2463 print " udiffs\c" 2464 2465 if [[ -x $WDIFF ]]; then 2466 $WDIFF -c "$COMM" \ 2467 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ 2468 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2469 if [[ $? -eq 0 ]]; then 2470 print " wdiffs\c" 2471 else 2472 print " wdiffs[fail]\c" 2473 fi 2474 fi 2475 2476 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2477 > $WDIR/$DIR/$F.sdiff.html 2478 print " sdiffs\c" 2479 2480 print " frames\c" 2481 2482 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2483 2484 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2485 2486 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2487 # renamed file: may also have differences 2488 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2489 elif [[ -f $nfile ]]; then 2490 # new file: count added lines 2491 difflines /dev/null $nfile > $WDIR/$DIR/$F.count 2492 elif [[ -f $ofile ]]; then 2493 # old file: count deleted lines 2494 difflines $ofile /dev/null > $WDIR/$DIR/$F.count 2495 fi 2496 2497 # 2498 # Now we generate the postscript for this file. We generate diffs 2499 # only in the event that there is delta, or the file is new (it seems 2500 # tree-killing to print out the contents of deleted files). 2501 # 2502 if [[ -f $nfile ]]; then 2503 ocr=$ofile 2504 [[ ! -f $ofile ]] && ocr=/dev/null 2505 2506 if [[ -z $mv_but_nodiff ]]; then 2507 textcomm=`getcomments text $P $PP` 2508 if [[ -x $CODEREVIEW ]]; then 2509 $CODEREVIEW -y "$textcomm" \ 2510 -e $ocr $nfile \ 2511 > /tmp/$$.psfile 2>/dev/null && 2512 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps 2513 if [[ $? -eq 0 ]]; then 2514 print " ps\c" 2515 else 2516 print " ps[fail]\c" 2517 fi 2518 fi 2519 fi 2520 fi 2521 2522 if [[ -f $ofile ]]; then 2523 source_to_html Old $PP < $ofile > $WDIR/$DIR/$F-.html 2524 print " old\c" 2525 fi 2526 2527 if [[ -f $nfile ]]; then 2528 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html 2529 print " new\c" 2530 fi 2531 2532 cd $OWD 2533 2534 print 2535done 2536 2537frame_nav_js > $WDIR/ancnav.js 2538frame_navigation > $WDIR/ancnav.html 2539 2540if [[ ! -f $WDIR/$WNAME.ps ]]; then 2541 print " Generating PDF: Skipped: no output available" 2542elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then 2543 print " Generating PDF: \c" 2544 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf 2545 print "Done." 2546else 2547 print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'" 2548fi 2549 2550# If we're in OpenSolaris mode and there's a closed dir under $WDIR, 2551# delete it - prevent accidental publishing of closed source 2552 2553if [[ -n "$Oflag" ]]; then 2554 /usr/bin/find $WDIR -type d -name closed -exec /bin/rm -rf {} \; 2555fi 2556 2557# Now build the index.html file that contains 2558# links to the source files and their diffs. 2559 2560cd $CWS 2561 2562# Save total changed lines for Code Inspection. 2563print "$TOTL" > $WDIR/TotalChangedLines 2564 2565print " index.html: \c" 2566INDEXFILE=$WDIR/index.html 2567exec 3<&1 # duplicate stdout to FD3. 2568exec 1<&- # Close stdout. 2569exec > $INDEXFILE # Open stdout to index file. 2570 2571print "$HTML<head>$STDHEAD" 2572print "<title>$WNAME</title>" 2573print "</head>" 2574print "<body id=\"SUNWwebrev\">" 2575print "<div class=\"summary\">" 2576print "<h2>Code Review for $WNAME</h2>" 2577 2578print "<table>" 2579 2580# 2581# Get the preparer's name: 2582# 2583# If the SCM detected is Mercurial, and the configuration property 2584# ui.username is available, use that, but be careful to properly escape 2585# angle brackets (HTML syntax characters) in the email address. 2586# 2587# Otherwise, use the current userid in the form "John Doe (jdoe)", but 2588# to maintain compatibility with passwd(4), we must support '&' substitutions. 2589# 2590preparer= 2591if [[ "$SCM_MODE" == mercurial ]]; then 2592 preparer=`hg showconfig ui.username 2>/dev/null` 2593 if [[ -n "$preparer" ]]; then 2594 preparer="$(echo "$preparer" | html_quote)" 2595 fi 2596fi 2597if [[ -z "$preparer" ]]; then 2598 preparer=$( 2599 $PERL -e ' 2600 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<); 2601 if ($login) { 2602 $gcos =~ s/\&/ucfirst($login)/e; 2603 printf "%s (%s)\n", $gcos, $login; 2604 } else { 2605 printf "(unknown)\n"; 2606 } 2607 ') 2608fi 2609 2610print "<tr><th>Prepared by:</th><td>$preparer on `date`</td></tr>" 2611print "<tr><th>Workspace:</th><td>$CWS" 2612if [[ -n $CWS_REV ]]; then 2613 print "(at $CWS_REV)" 2614fi 2615print "</td></tr>" 2616print "<tr><th>Compare against:</th><td>" 2617if [[ -n $parent_webrev ]]; then 2618 print "webrev at $parent_webrev" 2619else 2620 print "$PWS" 2621 if [[ -n $hg_parent_short ]]; then 2622 print "(at $hg_parent_short)" 2623 fi 2624fi 2625print "</td></tr>" 2626print "<tr><th>Summary of changes:</th><td>" 2627printCI $TOTL $TINS $TDEL $TMOD $TUNC 2628print "</td></tr>" 2629 2630if [[ -f $WDIR/$WNAME.patch ]]; then 2631 print "<tr><th>Patch of changes:</th><td>" 2632 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>" 2633fi 2634if [[ -f $WDIR/$WNAME.pdf ]]; then 2635 print "<tr><th>Printable review:</th><td>" 2636 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>" 2637fi 2638 2639if [[ -n "$iflag" ]]; then 2640 print "<tr><th>Author comments:</th><td><div>" 2641 cat /tmp/$$.include 2642 print "</div></td></tr>" 2643fi 2644print "</table>" 2645print "</div>" 2646 2647# 2648# Second pass through the files: generate the rest of the index file 2649# 2650cat $FLIST | while read LINE 2651do 2652 set - $LINE 2653 P=$1 2654 2655 if [[ $# == 2 ]]; then 2656 PP=$2 2657 oldname="$PP" 2658 else 2659 PP=$P 2660 oldname="" 2661 fi 2662 2663 mv_but_nodiff= 2664 cmp $WDIR/raw_files/old/$PP $WDIR/raw_files/new/$P > /dev/null 2>&1 2665 if [[ $? == 0 && -n "$oldname" ]]; then 2666 mv_but_nodiff=1 2667 fi 2668 2669 DIR=${P%/*} 2670 if [[ $DIR == $P ]]; then 2671 DIR="." # File at root of workspace 2672 fi 2673 2674 # Avoid processing the same file twice. 2675 # It's possible for renamed files to 2676 # appear twice in the file list 2677 2678 F=$WDIR/$P 2679 2680 print "<p>" 2681 2682 # If there's a diffs file, make diffs links 2683 2684 if [[ -f $F.cdiff.html ]]; then 2685 print "<a href=\"$P.cdiff.html\">Cdiffs</a>" 2686 print "<a href=\"$P.udiff.html\">Udiffs</a>" 2687 2688 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 2689 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 2690 fi 2691 2692 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 2693 2694 print "<a href=\"$P.frames.html\">Frames</a>" 2695 else 2696 print " ------ ------ ------" 2697 2698 if [[ -x $WDIFF ]]; then 2699 print " ------" 2700 fi 2701 2702 print " ------" 2703 fi 2704 2705 # If there's an old file, make the link 2706 2707 if [[ -f $F-.html ]]; then 2708 print "<a href=\"$P-.html\">Old</a>" 2709 else 2710 print " ---" 2711 fi 2712 2713 # If there's an new file, make the link 2714 2715 if [[ -f $F.html ]]; then 2716 print "<a href=\"$P.html\">New</a>" 2717 else 2718 print " ---" 2719 fi 2720 2721 if [[ -f $F.patch ]]; then 2722 print "<a href=\"$P.patch\">Patch</a>" 2723 else 2724 print " -----" 2725 fi 2726 2727 if [[ -f $WDIR/raw_files/new/$P ]]; then 2728 print "<a href=\"raw_files/new/$P\">Raw</a>" 2729 else 2730 print " ---" 2731 fi 2732 2733 print "<b>$P</b>" 2734 2735 # For renamed files, clearly state whether or not they are modified 2736 if [[ -n "$oldname" ]]; then 2737 if [[ -n "$mv_but_nodiff" ]]; then 2738 print "<i>(renamed only, was $oldname)</i>" 2739 else 2740 print "<i>(modified and renamed, was $oldname)</i>" 2741 fi 2742 fi 2743 2744 # If there's an old file, but no new file, the file was deleted 2745 if [[ -f $F-.html && ! -f $F.html ]]; then 2746 print " <i>(deleted)</i>" 2747 fi 2748 2749 # 2750 # Check for usr/closed and deleted_files/usr/closed 2751 # 2752 if [ ! -z "$Oflag" ]; then 2753 if [[ $P == usr/closed/* || \ 2754 $P == deleted_files/usr/closed/* ]]; then 2755 print " <i>Closed source: omitted from" \ 2756 "this review</i>" 2757 fi 2758 fi 2759 2760 print "</p>" 2761 # Insert delta comments 2762 2763 print "<blockquote><pre>" 2764 getcomments html $P $PP 2765 print "</pre>" 2766 2767 # Add additional comments comment 2768 2769 print "<!-- Add comments to explain changes in $P here -->" 2770 2771 # Add count of changes. 2772 2773 if [[ -f $F.count ]]; then 2774 cat $F.count 2775 rm $F.count 2776 fi 2777 2778 if [[ $SCM_MODE == "teamware" || 2779 $SCM_MODE == "mercurial" || 2780 $SCM_MODE == "unknown" ]]; then 2781 2782 # Include warnings for important file mode situations: 2783 # 1) New executable files 2784 # 2) Permission changes of any kind 2785 # 3) Existing executable files 2786 2787 old_mode= 2788 if [[ -f $WDIR/raw_files/old/$PP ]]; then 2789 old_mode=`get_file_mode $WDIR/raw_files/old/$PP` 2790 fi 2791 2792 new_mode= 2793 if [[ -f $WDIR/raw_files/new/$P ]]; then 2794 new_mode=`get_file_mode $WDIR/raw_files/new/$P` 2795 fi 2796 2797 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then 2798 print "<span class=\"chmod\">" 2799 print "<p>new executable file: mode $new_mode</p>" 2800 print "</span>" 2801 elif [[ -n "$old_mode" && -n "$new_mode" && 2802 "$old_mode" != "$new_mode" ]]; then 2803 print "<span class=\"chmod\">" 2804 print "<p>mode change: $old_mode to $new_mode</p>" 2805 print "</span>" 2806 elif [[ "$new_mode" = *[1357]* ]]; then 2807 print "<span class=\"chmod\">" 2808 print "<p>executable file: mode $new_mode</p>" 2809 print "</span>" 2810 fi 2811 fi 2812 2813 print "</blockquote>" 2814done 2815 2816print 2817print 2818print "<hr></hr>" 2819print "<p style=\"font-size: small\">" 2820print "This code review page was prepared using <b>$0</b>." 2821print "Webrev is maintained by the <a href=\"http://www.opensolaris.org\">" 2822print "OpenSolaris</a> project. The latest version may be obtained" 2823print "<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>" 2824print "</body>" 2825print "</html>" 2826 2827exec 1<&- # Close FD 1. 2828exec 1<&3 # dup FD 3 to restore stdout. 2829exec 3<&- # close FD 3. 2830 2831print "Done." 2832