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