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# ident "%Z%%M% %I% %E% SMI" 23# 24# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 25# Use is subject to license terms. 26# 27 28# 29# This script takes a file list and a workspace and builds a set of html files 30# suitable for doing a code review of source changes via a web page. 31# Documentation is available via the manual page, webrev.1, or just 32# type 'webrev -h'. 33# 34# Acknowledgements to contributors to webrev are listed in the webrev(1) 35# man page. 36# 37 38# 39# The following variable is set to SCCS delta date 20YY/MM/DD. 40# Note this will have to be changed in 2100 or when SCCS has support for 41# 4 digit years; whichever is the sooner! 42# 43WEBREV_UPDATED=20%E% 44 45REMOVED_COLOR=brown 46CHANGED_COLOR=blue 47NEW_COLOR=blue 48 49HTML='<?xml version="1.0"?> 50<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 51 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 52<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' 53 54FRAMEHTML='<?xml version="1.0"?> 55<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" 56 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> 57<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' 58 59STDHEAD='<meta http-equiv="cache-control" content="no-cache"></meta> 60<meta http-equiv="Pragma" content="no-cache"></meta> 61<meta http-equiv="Expires" content="-1"></meta> 62<!-- 63 Note to customizers: the body of the webrev is IDed as SUNWwebrev 64 to allow easy overriding by users of webrev via the userContent.css 65 mechanism available in some browsers. 66 67 For example, to have all "removed" information be red instead of 68 brown, set a rule in your userContent.css file like: 69 70 body#SUNWwebrev span.removed { color: red ! important; } 71--> 72<style type="text/css" media="screen"> 73body { 74 background-color: #eeeeee; 75} 76hr { 77 border: none 0; 78 border-top: 1px solid #aaa; 79 height: 1px; 80} 81div.summary { 82 font-size: .8em; 83 border-bottom: 1px solid #aaa; 84 padding-left: 1em; 85 padding-right: 1em; 86} 87div.summary h2 { 88 margin-bottom: 0.3em; 89} 90div.summary table th { 91 text-align: right; 92 vertical-align: top; 93 white-space: nowrap; 94} 95span.lineschanged { 96 font-size: 0.7em; 97} 98span.oldmarker { 99 color: red; 100 font-size: large; 101 font-weight: bold; 102} 103span.newmarker { 104 color: green; 105 font-size: large; 106 font-weight: bold; 107} 108span.removed { 109 color: brown; 110} 111span.changed { 112 color: blue; 113} 114span.new { 115 color: blue; 116 font-weight: bold; 117} 118span.chmod { 119 font-size: 0.7em; 120 color: #db7800; 121} 122a.print { font-size: x-small; } 123a:hover { background-color: #ffcc99; } 124</style> 125 126<style type="text/css" media="print"> 127pre { font-size: 0.8em; font-family: courier, monospace; } 128span.removed { color: #444; font-style: italic } 129span.changed { font-weight: bold; } 130span.new { font-weight: bold; } 131span.newmarker { font-size: 1.2em; font-weight: bold; } 132span.oldmarker { font-size: 1.2em; font-weight: bold; } 133a.print {display: none} 134hr { border: none 0; border-top: 1px solid #aaa; height: 1px; } 135</style> 136' 137 138# 139# UDiffs need a slightly different CSS rule for 'new' items (we don't 140# want them to be bolded as we do in cdiffs or sdiffs). 141# 142UDIFFCSS=' 143<style type="text/css" media="screen"> 144span.new { 145 color: blue; 146 font-weight: normal; 147} 148</style> 149' 150 151# 152# input_cmd | html_quote | output_cmd 153# or 154# html_quote filename | output_cmd 155# 156# Make a piece of source code safe for display in an HTML <pre> block. 157# 158html_quote() 159{ 160 sed -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" "$@" | expand 161} 162 163# 164# input_cmd | bug2url | output_cmd 165# 166# Scan for bugids and insert <a> links to the relevent bug database. 167# 168bug2url() 169{ 170 sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL'&\">&</a>|g' 171} 172 173# 174# input_cmd | sac2url | output_cmd 175# 176# Scan for ARC cases and insert <a> links to the relevent SAC database. 177# This is slightly complicated because inside the SWAN, SAC cases are 178# grouped by ARC: PSARC/2006/123. But on OpenSolaris.org, they are 179# referenced as 2006/123 (without labelling the ARC). 180# 181sac2url() 182{ 183 if [[ -z "$Oflag" ]]; then 184 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' 185 else 186 sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'/\2/\3\">\1 \2/\3</a>|g' 187 fi 188} 189 190# 191# strip_unchanged <infile> | output_cmd 192# 193# Removes chunks of sdiff documents that have not changed. This makes it 194# easier for a code reviewer to find the bits that have changed. 195# 196# Deleted lines of text are replaced by a horizontal rule. Some 197# identical lines are retained before and after the changed lines to 198# provide some context. The number of these lines is controlled by the 199# variable C in the $AWK script below. 200# 201# The script detects changed lines as any line that has a "<span class=" 202# string embedded (unchanged lines have no particular class and are not 203# part of a <span>). Blank lines (without a sequence number) are also 204# detected since they flag lines that have been inserted or deleted. 205# 206strip_unchanged() 207{ 208 $AWK ' 209 BEGIN { C = c = 20 } 210 NF == 0 || /<span class="/ { 211 if (c > C) { 212 c -= C 213 inx = 0 214 if (c > C) { 215 print "\n</pre><hr></hr><pre>" 216 inx = c % C 217 c = C 218 } 219 220 for (i = 0; i < c; i++) 221 print ln[(inx + i) % C] 222 } 223 c = 0; 224 print 225 next 226 } 227 { if (c >= C) { 228 ln[c % C] = $0 229 c++; 230 next; 231 } 232 c++; 233 print 234 } 235 END { if (c > (C * 2)) print "\n</pre><hr></hr>" } 236 237 ' $1 238} 239 240# 241# sdiff_to_html 242# 243# This function takes two files as arguments, obtains their diff, and 244# processes the diff output to present the files as an HTML document with 245# the files displayed side-by-side, differences shown in color. It also 246# takes a delta comment, rendered as an HTML snippet, as the third 247# argument. The function takes two files as arguments, then the name of 248# file, the path, and the comment. The HTML will be delivered on stdout, 249# e.g. 250# 251# $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \ 252# new/usr/src/tools/scripts/webrev.sh \ 253# webrev.sh usr/src/tools/scripts \ 254# '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567"> 255# 1234567</a> my bugid' > <file>.html 256# 257# framed_sdiff() is then called which creates $2.frames.html 258# in the webrev tree. 259# 260# FYI: This function is rather unusual in its use of awk. The initial 261# diff run produces conventional diff output showing changed lines mixed 262# with editing codes. The changed lines are ignored - we're interested in 263# the editing codes, e.g. 264# 265# 8c8 266# 57a61 267# 63c66,76 268# 68,93d80 269# 106d90 270# 108,110d91 271# 272# These editing codes are parsed by the awk script and used to generate 273# another awk script that generates HTML, e.g the above lines would turn 274# into something like this: 275# 276# BEGIN { printf "<pre>\n" } 277# function sp(n) {for (i=0;i<n;i++)printf "\n"} 278# function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0} 279# NR==8 {wl("#7A7ADD");next} 280# NR==54 {wl("#7A7ADD");sp(3);next} 281# NR==56 {wl("#7A7ADD");next} 282# NR==57 {wl("black");printf "\n"; next} 283# : : 284# 285# This script is then run on the original source file to generate the 286# HTML that corresponds to the source file. 287# 288# The two HTML files are then combined into a single piece of HTML that 289# uses an HTML table construct to present the files side by side. You'll 290# notice that the changes are color-coded: 291# 292# black - unchanged lines 293# blue - changed lines 294# bold blue - new lines 295# brown - deleted lines 296# 297# Blank lines are inserted in each file to keep unchanged lines in sync 298# (side-by-side). This format is familiar to users of sdiff(1) or 299# Teamware's filemerge tool. 300# 301sdiff_to_html() 302{ 303 diff -b $1 $2 > /tmp/$$.diffs 304 305 TNAME=$3 306 TPATH=$4 307 COMMENT=$5 308 309 # 310 # Now we have the diffs, generate the HTML for the old file. 311 # 312 $AWK ' 313 BEGIN { 314 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n" 315 printf "function removed() " 316 printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n" 317 printf "function changed() " 318 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n" 319 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n" 320} 321 /^</ {next} 322 /^>/ {next} 323 /^---/ {next} 324 325 { 326 split($1, a, /[cad]/) ; 327 if (index($1, "a")) { 328 if (a[1] == 0) { 329 n = split(a[2], r, /,/); 330 if (n == 1) 331 printf "BEGIN\t\t{sp(1)}\n" 332 else 333 printf "BEGIN\t\t{sp(%d)}\n",\ 334 (r[2] - r[1]) + 1 335 next 336 } 337 338 printf "NR==%s\t\t{", a[1] 339 n = split(a[2], r, /,/); 340 s = r[1]; 341 if (n == 1) 342 printf "bl();printf \"\\n\"; next}\n" 343 else { 344 n = r[2] - r[1] 345 printf "bl();sp(%d);next}\n",\ 346 (r[2] - r[1]) + 1 347 } 348 next 349 } 350 if (index($1, "d")) { 351 n = split(a[1], r, /,/); 352 n1 = r[1] 353 n2 = r[2] 354 if (n == 1) 355 printf "NR==%s\t\t{removed(); next}\n" , n1 356 else 357 printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2 358 next 359 } 360 if (index($1, "c")) { 361 n = split(a[1], r, /,/); 362 n1 = r[1] 363 n2 = r[2] 364 final = n2 365 d1 = 0 366 if (n == 1) 367 printf "NR==%s\t\t{changed();" , n1 368 else { 369 d1 = n2 - n1 370 printf "NR==%s,NR==%s\t{changed();" , n1, n2 371 } 372 m = split(a[2], r, /,/); 373 n1 = r[1] 374 n2 = r[2] 375 if (m > 1) { 376 d2 = n2 - n1 377 if (d2 > d1) { 378 if (n > 1) printf "if (NR==%d)", final 379 printf "sp(%d);", d2 - d1 380 } 381 } 382 printf "next}\n" ; 383 384 next 385 } 386 } 387 388 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" } 389 ' /tmp/$$.diffs > /tmp/$$.file1 390 391 # 392 # Now generate the HTML for the new file 393 # 394 $AWK ' 395 BEGIN { 396 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n" 397 printf "function new() " 398 printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n" 399 printf "function changed() " 400 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n" 401 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n" 402 } 403 404 /^</ {next} 405 /^>/ {next} 406 /^---/ {next} 407 408 { 409 split($1, a, /[cad]/) ; 410 if (index($1, "d")) { 411 if (a[2] == 0) { 412 n = split(a[1], r, /,/); 413 if (n == 1) 414 printf "BEGIN\t\t{sp(1)}\n" 415 else 416 printf "BEGIN\t\t{sp(%d)}\n",\ 417 (r[2] - r[1]) + 1 418 next 419 } 420 421 printf "NR==%s\t\t{", a[2] 422 n = split(a[1], r, /,/); 423 s = r[1]; 424 if (n == 1) 425 printf "bl();printf \"\\n\"; next}\n" 426 else { 427 n = r[2] - r[1] 428 printf "bl();sp(%d);next}\n",\ 429 (r[2] - r[1]) + 1 430 } 431 next 432 } 433 if (index($1, "a")) { 434 n = split(a[2], r, /,/); 435 n1 = r[1] 436 n2 = r[2] 437 if (n == 1) 438 printf "NR==%s\t\t{new() ; next}\n" , n1 439 else 440 printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2 441 next 442 } 443 if (index($1, "c")) { 444 n = split(a[2], r, /,/); 445 n1 = r[1] 446 n2 = r[2] 447 final = n2 448 d2 = 0; 449 if (n == 1) { 450 final = n1 451 printf "NR==%s\t\t{changed();" , n1 452 } else { 453 d2 = n2 - n1 454 printf "NR==%s,NR==%s\t{changed();" , n1, n2 455 } 456 m = split(a[1], r, /,/); 457 n1 = r[1] 458 n2 = r[2] 459 if (m > 1) { 460 d1 = n2 - n1 461 if (d1 > d2) { 462 if (n > 1) printf "if (NR==%d)", final 463 printf "sp(%d);", d1 - d2 464 } 465 } 466 printf "next}\n" ; 467 next 468 } 469 } 470 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" } 471 ' /tmp/$$.diffs > /tmp/$$.file2 472 473 # 474 # Post-process the HTML files by running them back through $AWK 475 # 476 html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html 477 478 html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html 479 480 # 481 # Now combine into a valid HTML file and side-by-side into a table 482 # 483 print "$HTML<head>$STDHEAD" 484 print "<title>$WNAME Sdiff $TPATH/$TNAME</title>" 485 print "</head><body id=\"SUNWwebrev\">" 486 print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>" 487 print "<pre>$COMMENT</pre>\n" 488 print "<table><tr valign=\"top\">" 489 print "<td><pre>" 490 491 strip_unchanged /tmp/$$.file1.html 492 493 print "</pre></td><td><pre>" 494 495 strip_unchanged /tmp/$$.file2.html 496 497 print "</pre></td>" 498 print "</tr></table>" 499 print "</body></html>" 500 501 framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \ 502 "$COMMENT" 503} 504 505 506# 507# framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment> 508# 509# Expects lefthand and righthand side html files created by sdiff_to_html. 510# We use insert_anchors() to augment those with HTML navigation anchors, 511# and then emit the main frame. Content is placed into: 512# 513# $WDIR/DIR/$TNAME.lhs.html 514# $WDIR/DIR/$TNAME.rhs.html 515# $WDIR/DIR/$TNAME.frames.html 516# 517# NOTE: We rely on standard usage of $WDIR and $DIR. 518# 519function framed_sdiff 520{ 521 typeset TNAME=$1 522 typeset TPATH=$2 523 typeset lhsfile=$3 524 typeset rhsfile=$4 525 typeset comments=$5 526 typeset RTOP 527 528 # Enable html files to access WDIR via a relative path. 529 RTOP=$(relative_dir $TPATH $WDIR) 530 531 # Make the rhs/lhs files and output the frameset file. 532 print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html 533 534 cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF 535 <script type="text/javascript" src="$RTOP/ancnav.js"></script> 536 </head> 537 <body id="SUNWwebrev" onkeypress="keypress(event);"> 538 <a name="0"></a> 539 <pre>$comments</pre><hr></hr> 540 EOF 541 542 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html 543 544 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html 545 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html 546 547 close='</body></html>' 548 549 print $close >> $WDIR/$DIR/$TNAME.lhs.html 550 print $close >> $WDIR/$DIR/$TNAME.rhs.html 551 552 print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html 553 print "<title>$WNAME Framed-Sdiff " \ 554 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html 555 cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF 556 <frameset rows="*,60"> 557 <frameset cols="50%,50%"> 558 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs"></frame> 559 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame> 560 </frameset> 561 <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0" 562 marginheight="0" name="nav"></frame> 563 <noframes> 564 <body id="SUNWwebrev"> 565 Alas 'frames' webrev requires that your browser supports frames 566 and has the feature enabled. 567 </body> 568 </noframes> 569 </frameset> 570 </html> 571 EOF 572} 573 574 575# 576# fix_postscript 577# 578# Merge codereview output files to a single conforming postscript file, by: 579# - removing all extraneous headers/trailers 580# - making the page numbers right 581# - removing pages devoid of contents which confuse some 582# postscript readers. 583# 584# From Casper. 585# 586function fix_postscript 587{ 588 infile=$1 589 590 cat > /tmp/$$.crmerge.pl << \EOF 591 592 print scalar(<>); # %!PS-Adobe--- 593 print "%%Orientation: Landscape\n"; 594 595 $pno = 0; 596 $doprint = 1; 597 598 $page = ""; 599 600 while (<>) { 601 next if (/^%%Pages:\s*\d+/); 602 603 if (/^%%Page:/) { 604 if ($pno == 0 || $page =~ /\)S/) { 605 # Header or single page containing text 606 print "%%Page: ? $pno\n" if ($pno > 0); 607 print $page; 608 $pno++; 609 } else { 610 # Empty page, skip it. 611 } 612 $page = ""; 613 $doprint = 1; 614 next; 615 } 616 617 # Skip from %%Trailer of one document to Endprolog 618 # %%Page of the next 619 $doprint = 0 if (/^%%Trailer/); 620 $page .= $_ if ($doprint); 621 } 622 623 if ($page =~ /\)S/) { 624 print "%%Page: ? $pno\n"; 625 print $page; 626 } else { 627 $pno--; 628 } 629 print "%%Trailer\n%%Pages: $pno\n"; 630EOF 631 632 $PERL /tmp/$$.crmerge.pl < $infile 633} 634 635 636# 637# input_cmd | insert_anchors | output_cmd 638# 639# Flag blocks of difference with sequentially numbered invisible 640# anchors. These are used to drive the frames version of the 641# sdiffs output. 642# 643# NOTE: Anchor zero flags the top of the file irrespective of changes, 644# an additional anchor is also appended to flag the bottom. 645# 646# The script detects changed lines as any line that has a "<span 647# class=" string embedded (unchanged lines have no class set and are 648# not part of a <span>. Blank lines (without a sequence number) 649# are also detected since they flag lines that have been inserted or 650# deleted. 651# 652function insert_anchors 653{ 654 $AWK ' 655 function ia() { 656 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++; 657 } 658 659 BEGIN { 660 anc=1; 661 inblock=1; 662 printf "<pre>\n"; 663 } 664 NF == 0 || /^<span class=/ { 665 if (inblock == 0) { 666 ia(); 667 inblock=1; 668 } 669 print; 670 next; 671 } 672 { 673 inblock=0; 674 print; 675 } 676 END { 677 ia(); 678 679 printf "<b style=\"font-size: large; color: red\">"; 680 printf "--- EOF ---</b>" 681 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n"; 682 printf "</pre>" 683 printf "<form name=\"eof\">"; 684 printf "<input name=\"value\" value=\"%d\" " \ 685 "type=\"hidden\"></input>", anc - 1; 686 printf "</form>"; 687 } 688 ' $1 689} 690 691 692# 693# relative_dir 694# 695# Print a relative return path from $1 to $2. For example if 696# $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview, 697# this function would print "../../../../". 698# 699# In the event that $1 is not in $2 a warning is printed to stderr, 700# and $2 is returned-- the result of this is that the resulting webrev 701# is not relocatable. 702# 703function relative_dir 704{ 705 typeset cur="${1##$2?(/)}" 706 typeset ret="" 707 if [[ $2 == $cur ]]; then # Should never happen. 708 # Should never happen. 709 print -u2 "\nWARNING: relative_dir: \"$1\" not relative " 710 print -u2 "to \"$2\". Check input paths. Framed webrev " 711 print -u2 "will not be relocatable!" 712 print $2 713 return 714 fi 715 716 while [[ -n ${cur} ]]; 717 do 718 cur=${cur%%*(/)*([!/])} 719 if [[ -z $ret ]]; then 720 ret=".." 721 else 722 ret="../$ret" 723 fi 724 done 725 print $ret 726} 727 728 729# 730# frame_nav_js 731# 732# Emit javascript for frame navigation 733# 734function frame_nav_js 735{ 736cat << \EOF 737var myInt; 738var scrolling=0; 739var sfactor = 3; 740var scount=10; 741 742function scrollByPix() { 743 if (scount<=0) { 744 sfactor*=1.2; 745 scount=10; 746 } 747 parent.lhs.scrollBy(0,sfactor); 748 parent.rhs.scrollBy(0,sfactor); 749 scount--; 750} 751 752function scrollToAnc(num) { 753 754 // Update the value of the anchor in the form which we use as 755 // storage for this value. setAncValue() will take care of 756 // correcting for overflow and underflow of the value and return 757 // us the new value. 758 num = setAncValue(num); 759 760 // Set location and scroll back a little to expose previous 761 // lines. 762 // 763 // Note that this could be improved: it is possible although 764 // complex to compute the x and y position of an anchor, and to 765 // scroll to that location directly. 766 // 767 parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num); 768 parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num); 769 770 parent.lhs.scrollBy(0,-30); 771 parent.rhs.scrollBy(0,-30); 772} 773 774function getAncValue() 775{ 776 return (parseInt(parent.nav.document.diff.real.value)); 777} 778 779function setAncValue(val) 780{ 781 if (val <= 0) { 782 val = 0; 783 parent.nav.document.diff.real.value = val; 784 parent.nav.document.diff.display.value = "BOF"; 785 return (val); 786 } 787 788 // 789 // The way we compute the max anchor value is to stash it 790 // inline in the left and right hand side pages-- it's the same 791 // on each side, so we pluck from the left. 792 // 793 maxval = parent.lhs.document.eof.value.value; 794 if (val < maxval) { 795 parent.nav.document.diff.real.value = val; 796 parent.nav.document.diff.display.value = val.toString(); 797 return (val); 798 } 799 800 // this must be: val >= maxval 801 val = maxval; 802 parent.nav.document.diff.real.value = val; 803 parent.nav.document.diff.display.value = "EOF"; 804 return (val); 805} 806 807function stopScroll() { 808 if (scrolling==1) { 809 clearInterval(myInt); 810 scrolling=0; 811 } 812} 813 814function startScroll() { 815 stopScroll(); 816 scrolling=1; 817 myInt=setInterval("scrollByPix()",10); 818} 819 820function handlePress(b) { 821 822 switch (b) { 823 case 1 : 824 scrollToAnc(-1); 825 break; 826 case 2 : 827 scrollToAnc(getAncValue() - 1); 828 break; 829 case 3 : 830 sfactor=-3; 831 startScroll(); 832 break; 833 case 4 : 834 sfactor=3; 835 startScroll(); 836 break; 837 case 5 : 838 scrollToAnc(getAncValue() + 1); 839 break; 840 case 6 : 841 scrollToAnc(999999); 842 break; 843 } 844} 845 846function handleRelease(b) { 847 stopScroll(); 848} 849 850function keypress(ev) { 851 var keynum; 852 var keychar; 853 854 if (window.event) { // IE 855 keynum = ev.keyCode; 856 } else if (ev.which) { // non-IE 857 keynum = ev.which; 858 } 859 860 keychar = String.fromCharCode(keynum); 861 862 if (keychar == "k") { 863 handlePress(2); 864 return (0); 865 } else if (keychar == "j" || keychar == " ") { 866 handlePress(5); 867 return (0); 868 } 869 return (1); 870} 871 872function ValidateDiffNum(){ 873 val = parent.nav.document.diff.display.value; 874 if (val == "EOF") { 875 scrollToAnc(999999); 876 return; 877 } 878 879 if (val == "BOF") { 880 scrollToAnc(0); 881 return; 882 } 883 884 i=parseInt(val); 885 if (isNaN(i)) { 886 parent.nav.document.diff.display.value = getAncValue(); 887 } else { 888 scrollToAnc(i); 889 } 890 return false; 891} 892 893EOF 894} 895 896# 897# frame_navigation 898# 899# Output anchor navigation file for framed sdiffs. 900# 901function frame_navigation 902{ 903 print "$HTML<head>$STDHEAD" 904 905 cat << \EOF 906<title>Anchor Navigation</title> 907<meta http-equiv="Content-Script-Type" content="text/javascript"> 908<meta http-equiv="Content-Type" content="text/html"> 909 910<style type="text/css"> 911 div.button td { padding-left: 5px; padding-right: 5px; 912 background-color: #eee; text-align: center; 913 border: 1px #444 outset; cursor: pointer; } 914 div.button a { font-weight: bold; color: black } 915 div.button td:hover { background: #ffcc99; } 916</style> 917EOF 918 919 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>" 920 921 cat << \EOF 922</head> 923<body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();" 924 onkeypress="keypress(event);"> 925 <noscript lang="javascript"> 926 <center> 927 <p><big>Framed Navigation controls require Javascript</big><br></br> 928 Either this browser is incompatable or javascript is not enabled</p> 929 </center> 930 </noscript> 931 <table width="100%" border="0" align="center"> 932 <tr> 933 <td valign="middle" width="25%">Diff navigation: 934 Use 'j' and 'k' for next and previous diffs; or use buttons 935 at right</td> 936 <td align="center" valign="top" width="50%"> 937 <div class="button"> 938 <table border="0" align="center"> 939 <tr> 940 <td> 941 <a onMouseDown="handlePress(1);return true;" 942 onMouseUp="handleRelease(1);return true;" 943 onMouseOut="handleRelease(1);return true;" 944 onClick="return false;" 945 title="Go to Beginning Of file">BOF</a></td> 946 <td> 947 <a onMouseDown="handlePress(3);return true;" 948 onMouseUp="handleRelease(3);return true;" 949 onMouseOut="handleRelease(3);return true;" 950 title="Scroll Up: Press and Hold to accelerate" 951 onClick="return false;">Scroll Up</a></td> 952 <td> 953 <a onMouseDown="handlePress(2);return true;" 954 onMouseUp="handleRelease(2);return true;" 955 onMouseOut="handleRelease(2);return true;" 956 title="Go to previous Diff" 957 onClick="return false;">Prev Diff</a> 958 </td></tr> 959 960 <tr> 961 <td> 962 <a onMouseDown="handlePress(6);return true;" 963 onMouseUp="handleRelease(6);return true;" 964 onMouseOut="handleRelease(6);return true;" 965 onClick="return false;" 966 title="Go to End Of File">EOF</a></td> 967 <td> 968 <a onMouseDown="handlePress(4);return true;" 969 onMouseUp="handleRelease(4);return true;" 970 onMouseOut="handleRelease(4);return true;" 971 title="Scroll Down: Press and Hold to accelerate" 972 onClick="return false;">Scroll Down</a></td> 973 <td> 974 <a onMouseDown="handlePress(5);return true;" 975 onMouseUp="handleRelease(5);return true;" 976 onMouseOut="handleRelease(5);return true;" 977 title="Go to next Diff" 978 onClick="return false;">Next Diff</a></td> 979 </tr> 980 </table> 981 </div> 982 </td> 983 <th valign="middle" width="25%"> 984 <form action="" name="diff" onsubmit="return ValidateDiffNum();"> 985 <input name="display" value="BOF" size="8" type="text"></input> 986 <input name="real" value="0" size="8" type="hidden"></input> 987 </form> 988 </th> 989 </tr> 990 </table> 991 </body> 992</html> 993EOF 994} 995 996 997 998# 999# diff_to_html <filename> <filepath> { U | C } <comment> 1000# 1001# Processes the output of diff to produce an HTML file representing either 1002# context or unified diffs. 1003# 1004diff_to_html() 1005{ 1006 TNAME=$1 1007 TPATH=$2 1008 DIFFTYPE=$3 1009 COMMENT=$4 1010 1011 print "$HTML<head>$STDHEAD" 1012 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>" 1013 1014 if [[ $DIFFTYPE == "U" ]]; then 1015 print "$UDIFFCSS" 1016 fi 1017 1018 cat <<-EOF 1019 </head> 1020 <body id="SUNWwebrev"> 1021 <a class="print" href="javascript:print()">Print this page</a> 1022 <pre>$COMMENT</pre> 1023 <pre> 1024 EOF 1025 1026 html_quote | $AWK ' 1027 /^--- new/ { next } 1028 /^\+\+\+ new/ { next } 1029 /^--- old/ { next } 1030 /^\*\*\* old/ { next } 1031 /^\*\*\*\*/ { next } 1032 /^-------/ { printf "<center><h1>%s</h1></center>\n", $0; next } 1033 /^\@\@.*\@\@$/ { printf "</pre><hr></hr><pre>\n"; 1034 printf "<span class=\"newmarker\">%s</span>\n", $0; 1035 next} 1036 1037 /^\*\*\*/ { printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0; 1038 next} 1039 /^---/ { printf "<span class=\"newmarker\">%s</span>\n", $0; 1040 next} 1041 /^\+/ {printf "<span class=\"new\">%s</span>\n", $0; next} 1042 /^!/ {printf "<span class=\"changed\">%s</span>\n", $0; next} 1043 /^-/ {printf "<span class=\"removed\">%s</span>\n", $0; next} 1044 {printf "%s\n", $0; next} 1045 ' 1046 1047 print "</pre></body></html>\n" 1048} 1049 1050 1051# 1052# source_to_html { new | old } <filename> 1053# 1054# Process a plain vanilla source file to transform it into an HTML file. 1055# 1056source_to_html() 1057{ 1058 WHICH=$1 1059 TNAME=$2 1060 1061 print "$HTML<head>$STDHEAD" 1062 print "<title>$WNAME $WHICH $TNAME</title>" 1063 print "<body id=\"SUNWwebrev\">" 1064 print "<pre>" 1065 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }' 1066 print "</pre></body></html>" 1067} 1068 1069# 1070# comments_from_teamware {text|html} parent-file child-file 1071# 1072# Find the first delta in the child that's not in the parent. Get the 1073# newest delta from the parent, get all deltas from the child starting 1074# with that delta, and then get all info starting with the second oldest 1075# delta in that list (the first delta unique to the child). 1076# 1077# This code adapted from Bill Shannon's "spc" script 1078# 1079comments_from_teamware() 1080{ 1081 fmt=$1 1082 pfile=$PWS/$2 1083 cfile=$CWS/$3 1084 1085 if [[ ! -f $PWS/${2%/*}/SCCS/s.${2##*/} && -n $RWS ]]; then 1086 pfile=$RWS/$2 1087 fi 1088 1089 if [[ -f $pfile ]]; then 1090 psid=$($SCCS prs -d:I: $pfile 2>/dev/null) 1091 else 1092 psid=1.1 1093 fi 1094 1095 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null) 1096 N=${#sids[@]} 1097 1098 nawkprg=' 1099 /^COMMENTS:/ {p=1; continue} 1100 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; } 1101 NF == 0u { continue } 1102 {if (p==0) continue; print $0 }' 1103 1104 if [[ $N -ge 2 ]]; then 1105 sid1=${sids[$((N-2))]} # Gets 2nd to last sid 1106 1107 if [[ $fmt == "text" ]]; then 1108 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1109 $AWK "$nawkprg" 1110 return 1111 fi 1112 1113 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \ 1114 html_quote | bug2url | sac2url | $AWK "$nawkprg" 1115 fi 1116} 1117 1118# 1119# comments_from_wx {text|html} filepath 1120# 1121# Given the pathname of a file, find its location in a "wx" active 1122# file list and print the following comment. Output is either text or 1123# HTML; if the latter, embedded bugids (sequence of 5 or more digits) 1124# are turned into URLs. 1125# 1126# This is also used with Mercurial and the file list provided by hg-active. 1127# 1128comments_from_wx() 1129{ 1130 typeset fmt=$1 1131 typeset p=$2 1132 1133 comm=`$AWK ' 1134 $1 == "'$p'" { 1135 do getline ; while (NF > 0) 1136 getline 1137 while (NF > 0) { print ; getline } 1138 exit 1139 }' < $wxfile` 1140 1141 if [[ -z $comm ]]; then 1142 comm="*** NO COMMENTS ***" 1143 fi 1144 1145 if [[ $fmt == "text" ]]; then 1146 print -- "$comm" 1147 return 1148 fi 1149 1150 print -- "$comm" | html_quote | bug2url | sac2url 1151 1152} 1153 1154# 1155# getcomments {text|html} filepath parentpath 1156# 1157# Fetch the comments depending on what SCM mode we're in. 1158# 1159getcomments() 1160{ 1161 typeset fmt=$1 1162 typeset p=$2 1163 typeset pp=$3 1164 1165 # 1166 # Mercurial support uses a file list in wx format, so this 1167 # will be used there, too 1168 # 1169 if [[ -n $wxfile ]]; then 1170 comments_from_wx $fmt $p 1171 else 1172 if [[ $SCM_MODE == "teamware" ]]; then 1173 comments_from_teamware $fmt $pp $p 1174 fi 1175 fi 1176} 1177 1178# 1179# printCI <total-changed> <inserted> <deleted> <modified> <unchanged> 1180# 1181# Print out Code Inspection figures similar to sccs-prt(1) format. 1182# 1183function printCI 1184{ 1185 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5 1186 typeset str 1187 if (( tot == 1 )); then 1188 str="line" 1189 else 1190 str="lines" 1191 fi 1192 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \ 1193 $tot $str $ins $del $mod $unc 1194} 1195 1196 1197# 1198# difflines <oldfile> <newfile> 1199# 1200# Calculate and emit number of added, removed, modified and unchanged lines, 1201# and total lines changed, the sum of added + removed + modified. 1202# 1203function difflines 1204{ 1205 integer tot mod del ins unc err 1206 typeset filename 1207 1208 eval $( diff -e $1 $2 | $AWK ' 1209 # Change range of lines: N,Nc 1210 /^[0-9]*,[0-9]*c$/ { 1211 n=split(substr($1,1,length($1)-1), counts, ","); 1212 if (n != 2) { 1213 error=2 1214 exit; 1215 } 1216 # 1217 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines. 1218 # following would be 5 - 3 = 2! Hence +1 for correction. 1219 # 1220 r=(counts[2]-counts[1])+1; 1221 1222 # 1223 # Now count replacement lines: each represents a change instead 1224 # of a delete, so increment c and decrement r. 1225 # 1226 while (getline != /^\.$/) { 1227 c++; 1228 r--; 1229 } 1230 # 1231 # If there were more replacement lines than original lines, 1232 # then r will be negative; in this case there are no deletions, 1233 # but there are r changes that should be counted as adds, and 1234 # since r is negative, subtract it from a and add it to c. 1235 # 1236 if (r < 0) { 1237 a-=r; 1238 c+=r; 1239 } 1240 1241 # 1242 # If there were more original lines than replacement lines, then 1243 # r will be positive; in this case, increment d by that much. 1244 # 1245 if (r > 0) { 1246 d+=r; 1247 } 1248 next; 1249 } 1250 1251 # Change lines: Nc 1252 /^[0-9].*c$/ { 1253 # The first line is a replacement; any more are additions. 1254 if (getline != /^\.$/) { 1255 c++; 1256 while (getline != /^\.$/) a++; 1257 } 1258 next; 1259 } 1260 1261 # Add lines: both Na and N,Na 1262 /^[0-9].*a$/ { 1263 while (getline != /^\.$/) a++; 1264 next; 1265 } 1266 1267 # Delete range of lines: N,Nd 1268 /^[0-9]*,[0-9]*d$/ { 1269 n=split(substr($1,1,length($1)-1), counts, ","); 1270 if (n != 2) { 1271 error=2 1272 exit; 1273 } 1274 # 1275 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines. 1276 # following would be 5 - 3 = 2! Hence +1 for correction. 1277 # 1278 r=(counts[2]-counts[1])+1; 1279 d+=r; 1280 next; 1281 } 1282 1283 # Delete line: Nd. For example 10d says line 10 is deleted. 1284 /^[0-9]*d$/ {d++; next} 1285 1286 # Should not get here! 1287 { 1288 error=1; 1289 exit; 1290 } 1291 1292 # Finish off - print results 1293 END { 1294 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n", 1295 (c+d+a), c, d, a, error); 1296 }' ) 1297 1298 # End of $AWK, Check to see if any trouble occurred. 1299 if (( $? > 0 || err > 0 )); then 1300 print "Unexpected Error occurred reading" \ 1301 "\`diff -e $1 $2\`: \$?=$?, err=" $err 1302 return 1303 fi 1304 1305 # Accumulate totals 1306 (( TOTL += tot )) 1307 (( TMOD += mod )) 1308 (( TDEL += del )) 1309 (( TINS += ins )) 1310 # Calculate unchanged lines 1311 unc=`wc -l < $1` 1312 if (( unc > 0 )); then 1313 (( unc -= del + mod )) 1314 (( TUNC += unc )) 1315 fi 1316 # print summary 1317 print "<span class=\"lineschanged\">" 1318 printCI $tot $ins $del $mod $unc 1319 print "</span>" 1320} 1321 1322 1323# 1324# flist_from_wx 1325# 1326# Sets up webrev to source its information from a wx-formatted file. 1327# Sets the global 'wxfile' variable. 1328# 1329function flist_from_wx 1330{ 1331 typeset argfile=$1 1332 if [[ -n ${argfile%%/*} ]]; then 1333 # 1334 # If the wx file pathname is relative then make it absolute 1335 # because the webrev does a "cd" later on. 1336 # 1337 wxfile=$PWD/$argfile 1338 else 1339 wxfile=$argfile 1340 fi 1341 1342 $AWK '{ c = 1; print; 1343 while (getline) { 1344 if (NF == 0) { c = -c; continue } 1345 if (c > 0) print 1346 } 1347 }' $wxfile > $FLIST 1348 1349 print " Done." 1350} 1351 1352# 1353# flist_from_teamware [ <args-to-putback-n> ] 1354# 1355# Generate the file list by extracting file names from a putback -n. Some 1356# names may come from the "update/create" messages and others from the 1357# "currently checked out" warning. Renames are detected here too. Extract 1358# values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback 1359# -n as well, but remove them if they are already defined. 1360# 1361function flist_from_teamware 1362{ 1363 if [[ -n $codemgr_parent && -z $parent_webrev ]]; then 1364 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then 1365 print -u2 "parent $codemgr_parent doesn't look like a" \ 1366 "valid teamware workspace" 1367 exit 1 1368 fi 1369 parent_args="-p $codemgr_parent" 1370 fi 1371 1372 print " File list from: 'putback -n $parent_args $*' ... \c" 1373 1374 putback -n $parent_args $* 2>&1 | 1375 $AWK ' 1376 /^update:|^create:/ {print $2} 1377 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)} 1378 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)} 1379 /^The following files are currently checked out/ {p = 1; continue} 1380 NF == 0 {p=0 ; continue} 1381 /^rename/ {old=$3} 1382 $1 == "to:" {print $2, old} 1383 /^"/ {continue} 1384 p == 1 {print $1}' | 1385 sort -r -k 1,1 -u | sort > $FLIST 1386 1387 print " Done." 1388} 1389 1390# 1391# Call hg-active to get the active list output in the wx active list format 1392# 1393function hg_active_wxfile 1394{ 1395 typeset child=$1 1396 typeset parent=$2 1397 1398 TMPFLIST=/tmp/$$.active 1399 $HG_ACTIVE -w $child -p $parent > $TMPFLIST 1400 wxfile=$TMPFLIST 1401} 1402 1403# 1404# flist_from_mercurial 1405# Call hg-active to get a wx-style active list, and hand it off to 1406# flist_from_wx 1407# 1408function flist_from_mercurial 1409{ 1410 typeset child=$1 1411 typeset parent=$2 1412 1413 print " File list from: hg-active -p $parent ...\c" 1414 1415 if [[ ! -x $HG_ACTIVE ]]; then 1416 print # Blank line for the \c above 1417 print -u2 "Error: hg-active tool not found. Exiting" 1418 exit 1 1419 fi 1420 hg_active_wxfile $child $parent 1421 1422 # flist_from_wx prints the Done, so we don't have to. 1423 flist_from_wx $TMPFLIST 1424} 1425 1426# 1427# flist_from_subversion 1428# 1429# Generate the file list by extracting file names from svn status. 1430# 1431function flist_from_subversion 1432{ 1433 CWS=$1 1434 OLDPWD=$2 1435 1436 cd $CWS 1437 print -u2 " File list from: svn status ... \c" 1438 svn status | $AWK '/^[ACDMR]/ { print $NF }' > $FLIST 1439 print -u2 " Done." 1440 cd $OLDPWD 1441} 1442 1443function env_from_flist 1444{ 1445 [[ -r $FLIST ]] || return 1446 1447 # 1448 # Use "eval" to set env variables that are listed in the file 1449 # list. Then copy those into our local versions of those 1450 # variables if they have not been set already. 1451 # 1452 eval `sed -e "s/#.*$//" $FLIST | grep = ` 1453 1454 if [[ -z $codemgr_ws && -n $CODEMGR_WS ]]; then 1455 codemgr_ws=$CODEMGR_WS 1456 export CODEMGR_WS 1457 fi 1458 1459 # 1460 # Check to see if CODEMGR_PARENT is set in the flist file. 1461 # 1462 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then 1463 codemgr_parent=$CODEMGR_PARENT 1464 export CODEMGR_PARENT 1465 fi 1466} 1467 1468function look_for_prog 1469{ 1470 typeset path 1471 typeset ppath 1472 typeset progname=$1 1473 1474 ppath=$PATH 1475 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin 1476 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin 1477 ppath=$ppath:/opt/onbld/bin/`uname -p` 1478 1479 PATH=$ppath prog=`whence $progname` 1480 if [[ -n $prog ]]; then 1481 print $prog 1482 fi 1483} 1484 1485function get_file_mode 1486{ 1487 $PERL -e ' 1488 if (@stat = stat($ARGV[0])) { 1489 $mode = $stat[2] & 0777; 1490 printf "%03o\n", $mode; 1491 exit 0; 1492 } else { 1493 exit 1; 1494 } 1495 ' $1 1496} 1497 1498function build_old_new_teamware 1499{ 1500 typeset olddir="$1" 1501 typeset newdir="$2" 1502 1503 # If the child's version doesn't exist then 1504 # get a readonly copy. 1505 1506 if [[ ! -f $CWS/$DIR/$F && -f $CWS/$DIR/SCCS/s.$F ]]; then 1507 $SCCS get -s -p $CWS/$DIR/$F > $CWS/$DIR/$F 1508 fi 1509 1510 # The following two sections propagate file permissions the 1511 # same way SCCS does. If the file is already under version 1512 # control, always use permissions from the SCCS/s.file. If 1513 # the file is not under SCCS control, use permissions from the 1514 # working copy. In all cases, the file copied to the webrev 1515 # is set to read only, and group/other permissions are set to 1516 # match those of the file owner. This way, even if the file 1517 # is currently checked out, the webrev will display the final 1518 # permissions that would result after check in. 1519 1520 # 1521 # Snag new version of file. 1522 # 1523 rm -f $newdir/$DIR/$F 1524 cp $CWS/$DIR/$F $newdir/$DIR/$F 1525 if [[ -f $CWS/$DIR/SCCS/s.$F ]]; then 1526 chmod `get_file_mode $CWS/$DIR/SCCS/s.$F` \ 1527 $newdir/$DIR/$F 1528 fi 1529 chmod u-w,go=u $newdir/$DIR/$F 1530 1531 # 1532 # Get the parent's version of the file. First see whether the 1533 # child's version is checked out and get the parent's version 1534 # with keywords expanded or unexpanded as appropriate. 1535 # 1536 if [[ -f $PWS/$PDIR/$PF && ! -f $PWS/$PDIR/SCCS/s.$PF && \ 1537 ! -f $PWS/$PDIR/SCCS/p.$PF ]]; then 1538 # Parent is not a real workspace, but just a raw 1539 # directory tree - use the file that's there as 1540 # the old file. 1541 1542 rm -f $olddir/$PDIR/$PF 1543 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1544 else 1545 if [[ -f $PWS/$PDIR/SCCS/s.$PF ]]; then 1546 real_parent=$PWS 1547 else 1548 real_parent=$RWS 1549 fi 1550 1551 rm -f $olddir/$PDIR/$PF 1552 1553 if [[ -f $real_parent/$PDIR/$PF ]]; then 1554 if [ -f $CWS/$DIR/SCCS/p.$F ]; then 1555 $SCCS get -s -p -k $real_parent/$PDIR/$PF > \ 1556 $olddir/$PDIR/$PF 1557 else 1558 $SCCS get -s -p $real_parent/$PDIR/$PF > \ 1559 $olddir/$PDIR/$PF 1560 fi 1561 chmod `get_file_mode $real_parent/$PDIR/SCCS/s.$PF` \ 1562 $olddir/$PDIR/$PF 1563 fi 1564 fi 1565 if [[ -f $olddir/$PDIR/$PF ]]; then 1566 chmod u-w,go=u $olddir/$PDIR/$PF 1567 fi 1568} 1569 1570function build_old_new_mercurial 1571{ 1572 typeset olddir="$1" 1573 typeset newdir="$2" 1574 typeset old_mode= 1575 typeset new_mode= 1576 typeset file 1577 1578 # 1579 # Get old file mode, from the parent revision manifest entry. 1580 # Mercurial only stores a "file is executable" flag, but the 1581 # manifest will display an octal mode "644" or "755". 1582 # 1583 if [[ "$PDIR" == "." ]]; then 1584 file="$PF" 1585 else 1586 file="$PDIR/$PF" 1587 fi 1588 file=`echo $file | sed 's#/#\\\/#g'` 1589 # match the exact filename, and return only the permission digits 1590 old_mode=`sed -n -e "/^\\(...\\) . ${file}$/s//\\1/p" \ 1591 < $HG_PARENT_MANIFEST` 1592 1593 # 1594 # Get new file mode, directly from the filesystem. 1595 # Normalize the mode to match Mercurial's behavior. 1596 # 1597 new_mode=`get_file_mode $CWS/$DIR/$F` 1598 if [[ -n "$new_mode" ]]; then 1599 if [[ "$new_mode" = *[1357]* ]]; then 1600 new_mode=755 1601 else 1602 new_mode=644 1603 fi 1604 fi 1605 1606 # 1607 # new version of the file. 1608 # 1609 rm -rf $newdir/$DIR/$F 1610 if [[ -e $CWS/$DIR/$F ]]; then 1611 cp $CWS/$DIR/$F $newdir/$DIR/$F 1612 if [[ -n $new_mode ]]; then 1613 chmod $new_mode $newdir/$DIR/$F 1614 else 1615 # should never happen 1616 print -u2 "ERROR: set mode of $newdir/$DIR/$F" 1617 fi 1618 fi 1619 1620 # 1621 # parent's version of the file 1622 # 1623 # Note that we get this from the last version common to both 1624 # ourselves and the parent. References are via $CWS since we have no 1625 # guarantee that the parent workspace is reachable via the filesystem. 1626 # 1627 if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then 1628 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1629 elif [[ -n $HG_PARENT ]]; then 1630 hg cat -R $CWS -r $HG_PARENT $CWS/$PDIR/$PF > \ 1631 $olddir/$PDIR/$PF 2>/dev/null 1632 1633 if [ $? -ne 0 ]; then 1634 rm -f $olddir/$PDIR/$PF 1635 else 1636 if [[ -n $old_mode ]]; then 1637 chmod $old_mode $olddir/$PDIR/$PF 1638 else 1639 # should never happen 1640 print -u2 "ERROR: set mode of $olddir/$PDIR/$PF" 1641 fi 1642 fi 1643 fi 1644} 1645 1646function build_old_new_subversion 1647{ 1648 typeset olddir="$1" 1649 typeset newdir="$2" 1650 1651 # Snag new version of file. 1652 rm -f $newdir/$DIR/$F 1653 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F 1654 1655 if [[ -n $PWS && -e $PWS/$PDIR/$PF ]]; then 1656 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1657 else 1658 # Get the parent's version of the file. 1659 svn status $CWS/$DIR/$F | read stat file 1660 if [[ $stat != "A" ]]; then 1661 svn cat -r BASE $CWS/$DIR/$F > $olddir/$PDIR/$PF 1662 fi 1663 fi 1664} 1665 1666function build_old_new_unknown 1667{ 1668 typeset olddir="$1" 1669 typeset newdir="$2" 1670 1671 # 1672 # Snag new version of file. 1673 # 1674 rm -f $newdir/$DIR/$F 1675 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F 1676 1677 # 1678 # Snag the parent's version of the file. 1679 # 1680 if [[ -f $PWS/$PDIR/$PF ]]; then 1681 rm -f $olddir/$PDIR/$PF 1682 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF 1683 fi 1684} 1685 1686function build_old_new 1687{ 1688 typeset WDIR=$1 1689 typeset PWS=$2 1690 typeset PDIR=$3 1691 typeset PF=$4 1692 typeset CWS=$5 1693 typeset DIR=$6 1694 typeset F=$7 1695 1696 typeset olddir="$WDIR/raw_files/old" 1697 typeset newdir="$WDIR/raw_files/new" 1698 1699 mkdir -p $olddir/$PDIR 1700 mkdir -p $newdir/$DIR 1701 1702 if [[ $SCM_MODE == "teamware" ]]; then 1703 build_old_new_teamware "$olddir" "$newdir" 1704 elif [[ $SCM_MODE == "mercurial" ]]; then 1705 build_old_new_mercurial "$olddir" "$newdir" 1706 elif [[ $SCM_MODE == "subversion" ]]; then 1707 build_old_new_subversion "$olddir" "$newdir" 1708 elif [[ $SCM_MODE == "unknown" ]]; then 1709 build_old_new_unknown "$olddir" "$newdir" 1710 fi 1711 1712 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then 1713 print "*** Error: file not in parent or child" 1714 return 1 1715 fi 1716 return 0 1717} 1718 1719 1720# 1721# Usage message. 1722# 1723function usage 1724{ 1725 print 'Usage:\twebrev [common-options] 1726 webrev [common-options] ( <file> | - ) 1727 webrev [common-options] -w <wx file> 1728 1729Options: 1730 -O: Print bugids/arc cases suitable for OpenSolaris. 1731 -i <filename>: Include <filename> in the index.html file. 1732 -o <outdir>: Output webrev to specified directory. 1733 -p <compare-against>: Use specified parent wkspc or basis for comparison 1734 -w <wxfile>: Use specified wx active file. 1735 1736Environment: 1737 WDIR: Control the output directory. 1738 WEBREV_BUGURL: Control the URL prefix for bugids. 1739 WEBREV_SACURL: Control the URL prefix for ARC cases. 1740 1741SCM Specific Options: 1742 TeamWare: webrev [common-options] -l [arguments to 'putback'] 1743 1744SCM Environment: 1745 CODEMGR_WS: Workspace location. 1746 CODEMGR_PARENT: Parent workspace location. 1747' 1748 1749 exit 2 1750} 1751 1752# 1753# 1754# Main program starts here 1755# 1756# 1757 1758trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15 1759 1760set +o noclobber 1761 1762PATH=$(dirname $(whence $0)):$PATH 1763 1764[[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` 1765[[ -z $WX ]] && WX=`look_for_prog wx` 1766[[ -z $HG_ACTIVE ]] && HG_ACTIVE=`look_for_prog hg-active` 1767[[ -z $WHICH_SCM ]] && WHICH_SCM=`look_for_prog which_scm` 1768[[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` 1769[[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf` 1770[[ -z $PERL ]] && PERL=`look_for_prog perl` 1771[[ -z $SCCS ]] && SCCS=`look_for_prog sccs` 1772[[ -z $AWK ]] && AWK=`look_for_prog nawk` 1773[[ -z $AWK ]] && AWK=`look_for_prog gawk` 1774[[ -z $AWK ]] && AWK=`look_for_prog awk` 1775 1776 1777if [[ ! -x $PERL ]]; then 1778 print -u2 "Error: No perl interpreter found. Exiting." 1779 exit 1 1780fi 1781 1782if [[ ! -x $WHICH_SCM ]]; then 1783 print -u2 "Error: Could not find which_scm. Exiting." 1784 exit 1 1785fi 1786 1787# 1788# These aren't fatal, but we want to note them to the user. 1789# We don't warn on the absence of 'wx' until later when we've 1790# determined that we actually need to try to invoke it. 1791# 1792[[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found." 1793[[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found." 1794[[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found." 1795 1796# Declare global total counters. 1797integer TOTL TINS TDEL TMOD TUNC 1798 1799flist_mode= 1800flist_file= 1801iflag= 1802oflag= 1803pflag= 1804lflag= 1805wflag= 1806Oflag= 1807while getopts "i:o:p:lwO" opt 1808do 1809 case $opt in 1810 i) iflag=1 1811 INCLUDE_FILE=$OPTARG;; 1812 1813 o) oflag=1 1814 WDIR=$OPTARG;; 1815 1816 p) pflag=1 1817 codemgr_parent=$OPTARG;; 1818 1819 # 1820 # If -l has been specified, we need to abort further options 1821 # processing, because subsequent arguments are going to be 1822 # arguments to 'putback -n'. 1823 # 1824 l) lflag=1 1825 break;; 1826 1827 w) wflag=1;; 1828 1829 O) Oflag=1;; 1830 1831 ?) usage;; 1832 esac 1833done 1834 1835FLIST=/tmp/$$.flist 1836 1837if [[ -n $wflag && -n $lflag ]]; then 1838 usage 1839fi 1840 1841# 1842# If this manually set as the parent, and it appears to be an earlier webrev, 1843# then note that fact and set the parent to the raw_files/new subdirectory. 1844# 1845if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then 1846 parent_webrev="$codemgr_parent" 1847 codemgr_parent="$codemgr_parent/raw_files/new" 1848fi 1849 1850if [[ -z $wflag && -z $lflag ]]; then 1851 shift $(($OPTIND - 1)) 1852 1853 if [[ $1 == "-" ]]; then 1854 cat > $FLIST 1855 flist_mode="stdin" 1856 flist_done=1 1857 shift 1858 elif [[ -n $1 ]]; then 1859 if [[ ! -r $1 ]]; then 1860 print -u2 "$1: no such file or not readable" 1861 usage 1862 fi 1863 cat $1 > $FLIST 1864 flist_mode="file" 1865 flist_file=$1 1866 flist_done=1 1867 shift 1868 else 1869 flist_mode="auto" 1870 fi 1871fi 1872 1873# 1874# Before we go on to further consider -l and -w, work out which SCM we think 1875# is in use. 1876# 1877$WHICH_SCM | read SCM_MODE junk || exit 1 1878case "$SCM_MODE" in 1879teamware|mercurial|subversion) 1880 ;; 1881unknown) 1882 if [[ $flist_mode == "auto" ]]; then 1883 print -u2 "Unable to determine SCM in use and file list not specified" 1884 print -u2 "See which_scm(1) for SCM detection information." 1885 exit 1 1886 fi 1887 ;; 1888*) 1889 if [[ $flist_mode == "auto" ]]; then 1890 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified" 1891 exit 1 1892 fi 1893 ;; 1894esac 1895 1896print -u2 " SCM detected: $SCM_MODE" 1897 1898if [[ -n $lflag ]]; then 1899 # 1900 # If the -l flag is given instead of the name of a file list, 1901 # then generate the file list by extracting file names from a 1902 # putback -n. 1903 # 1904 shift $(($OPTIND - 1)) 1905 if [[ $SCM_MODE == "teamware" ]]; then 1906 flist_from_teamware "$*" 1907 else 1908 print -u2 -- "Error: -l option only applies to TeamWare" 1909 exit 1 1910 fi 1911 flist_done=1 1912 shift $# 1913elif [[ -n $wflag ]]; then 1914 # 1915 # If the -w is given then assume the file list is in Bonwick's "wx" 1916 # command format, i.e. pathname lines alternating with SCCS comment 1917 # lines with blank lines as separators. Use the SCCS comments later 1918 # in building the index.html file. 1919 # 1920 shift $(($OPTIND - 1)) 1921 wxfile=$1 1922 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then 1923 if [[ -r $CODEMGR_WS/wx/active ]]; then 1924 wxfile=$CODEMGR_WS/wx/active 1925 fi 1926 fi 1927 1928 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \ 1929 "be auto-detected (check \$CODEMGR_WS)" && exit 1 1930 1931 if [[ ! -r $wxfile ]]; then 1932 print -u2 "$wxfile: no such file or not readable" 1933 usage 1934 fi 1935 1936 print -u2 " File list from: wx 'active' file '$wxfile' ... \c" 1937 flist_from_wx $wxfile 1938 flist_done=1 1939 if [[ -n "$*" ]]; then 1940 shift 1941 fi 1942elif [[ $flist_mode == "stdin" ]]; then 1943 print -u2 " File list from: standard input" 1944elif [[ $flist_mode == "file" ]]; then 1945 print -u2 " File list from: $flist_file" 1946fi 1947 1948if [[ $# -gt 0 ]]; then 1949 print -u2 "WARNING: unused arguments: $*" 1950fi 1951 1952if [[ $SCM_MODE == "teamware" ]]; then 1953 # 1954 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can 1955 # be set in a number of ways, in decreasing precedence: 1956 # 1957 # 1) on the command line (only for the parent) 1958 # 2) in the user environment 1959 # 3) in the flist 1960 # 4) automatically based on the workspace (only for the parent) 1961 # 1962 1963 # 1964 # Here is case (2): the user environment 1965 # 1966 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1967 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then 1968 print -u2 "$codemgr_ws: no such workspace" 1969 exit 1 1970 fi 1971 1972 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 1973 codemgr_parent=$CODEMGR_PARENT 1974 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then 1975 print -u2 "$codemgr_parent: no such directory" 1976 exit 1 1977 fi 1978 1979 # 1980 # If we're in auto-detect mode and we haven't already gotten the file 1981 # list, then see if we can get it by probing for wx. 1982 # 1983 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then 1984 if [[ ! -x $WX ]]; then 1985 print -u2 "WARNING: wx not found!" 1986 fi 1987 1988 # 1989 # We need to use wx list -w so that we get renamed files, etc. 1990 # but only if a wx active file exists-- otherwise wx will 1991 # hang asking us to initialize our wx information. 1992 # 1993 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then 1994 print -u2 " File list from: 'wx list -w' ... \c" 1995 $WX list -w > $FLIST 1996 $WX comments > /tmp/$$.wx_comments 1997 wxfile=/tmp/$$.wx_comments 1998 print -u2 "done" 1999 flist_done=1 2000 fi 2001 fi 2002 2003 # 2004 # If by hook or by crook we've gotten a file list by now (perhaps 2005 # from the command line), eval it to extract environment variables from 2006 # it: This is step (3). 2007 # 2008 env_from_flist 2009 2010 # 2011 # Continuing step (3): If we still have no file list, we'll try to get 2012 # it from teamware. 2013 # 2014 if [[ -z $flist_done ]]; then 2015 flist_from_teamware 2016 env_from_flist 2017 fi 2018 2019 # 2020 # (4) If we still don't have a value for codemgr_parent, get it 2021 # from workspace. 2022 # 2023 [[ -z $codemgr_ws ]] && codemgr_ws=`workspace name` 2024 [[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent` 2025 if [[ ! -d $codemgr_parent ]]; then 2026 print -u2 "$CODEMGR_PARENT: no such parent workspace" 2027 exit 1 2028 fi 2029 2030 # 2031 # Observe true directory name of CODEMGR_WS, as used later in 2032 # webrev title. 2033 # 2034 codemgr_ws=$(cd $codemgr_ws;print $PWD) 2035 2036 # 2037 # Reset CODEMGR_WS to make sure teamware commands are happy. 2038 # 2039 CODEMGR_WS=$codemgr_ws 2040 CWS=$codemgr_ws 2041 PWS=$codemgr_parent 2042 2043 [[ -n $parent_webrev ]] && RWS=$(workspace parent $CWS) 2044 2045elif [[ $SCM_MODE == "mercurial" ]]; then 2046 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && \ 2047 codemgr_ws=`hg root -R $CODEMGR_WS 2>/dev/null` 2048 2049 [[ -z $codemgr_ws ]] && codemgr_ws=`hg root 2>/dev/null` 2050 2051 # 2052 # Parent can either be specified with -p 2053 # Specified with CODEMGR_PARENT in the environment 2054 # or taken from hg's default path. 2055 # 2056 2057 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then 2058 codemgr_parent=$CODEMGR_PARENT 2059 fi 2060 2061 if [[ -z $codemgr_parent ]]; then 2062 codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null` 2063 fi 2064 2065 CWS_REV=`hg parent -R $codemgr_ws --template '{node|short}' 2>/dev/null` 2066 CWS=$codemgr_ws 2067 PWS=$codemgr_parent 2068 2069 # 2070 # If the parent is a webrev, we want to do some things against 2071 # the natural workspace parent (file list, comments, etc) 2072 # 2073 if [[ -n $parent_webrev ]]; then 2074 real_parent=$(hg path -R $codemgr_ws default 2>/dev/null) 2075 else 2076 real_parent=$PWS 2077 fi 2078 2079 # 2080 # If hg-active exists, then we run it. In the case of no explicit 2081 # flist given, we'll use it for our comments. In the case of an 2082 # explicit flist given we'll try to use it for comments for any 2083 # files mentioned in the flist. 2084 # 2085 if [[ -z $flist_done ]]; then 2086 flist_from_mercurial $CWS $real_parent 2087 flist_done=1 2088 fi 2089 2090 # 2091 # If we have a file list now, pull out any variables set 2092 # therein. We do this now (rather than when we possibly use 2093 # hg-active to find comments) to avoid stomping specifications 2094 # in the user-specified flist. 2095 # 2096 if [[ -n $flist_done ]]; then 2097 env_from_flist 2098 fi 2099 2100 # 2101 # Only call hg-active if we don't have a wx formatted file already 2102 # 2103 if [[ -x $HG_ACTIVE && -z $wxfile ]]; then 2104 print " Comments from: hg-active -p $real_parent ...\c" 2105 hg_active_wxfile $CWS $real_parent 2106 print " Done." 2107 fi 2108 2109 # 2110 # At this point we must have a wx flist either from hg-active, 2111 # or in general. Use it to try and find our parent revision, 2112 # if we don't have one. 2113 # 2114 if [[ -z $HG_PARENT ]]; then 2115 eval `sed -e "s/#.*$//" $wxfile | grep HG_PARENT=` 2116 fi 2117 2118 # 2119 # If we still don't have a parent, we must have been given a 2120 # wx-style active list with no HG_PARENT specification, run 2121 # hg-active and pull an HG_PARENT out of it, ignore the rest. 2122 # 2123 if [[ -z $HG_PARENT && -x $HG_ACTIVE ]]; then 2124 $HG_ACTIVE -w $codemgr_ws -p $real_parent | \ 2125 eval `sed -e "s/#.*$//" | grep HG_PARENT=` 2126 elif [[ -z $HG_PARENT ]]; then 2127 print -u2 "Error: Cannot discover parent revision" 2128 exit 1 2129 fi 2130elif [[ $SCM_MODE == "subversion" ]]; then 2131 if [[ -n $CODEMGR_WS && -d $CODEMGR_WS/.svn ]]; then 2132 CWS=$CODEMGR_WS 2133 else 2134 svn info | while read line; do 2135 if [[ $line == "URL: "* ]]; then 2136 url=${line#URL: } 2137 elif [[ $line == "Repository Root: "* ]]; then 2138 repo=${line#Repository Root: } 2139 fi 2140 done 2141 2142 rel=${url#$repo} 2143 CWS=${PWD%$rel} 2144 fi 2145 2146 # 2147 # We only will have a real parent workspace in the case one 2148 # was specified (be it an older webrev, or another checkout). 2149 # 2150 [[ -n $codemgr_parent ]] && PWS=$codemgr_parent 2151 2152 if [[ -z $flist_done && $flist_mode == "auto" ]]; then 2153 flist_from_subversion $CWS $OLDPWD 2154 fi 2155else 2156 if [[ $SCM_MODE == "unknown" ]]; then 2157 print -u2 " Unknown type of SCM in use" 2158 else 2159 print -u2 " Unsupported SCM in use: $SCM_MODE" 2160 fi 2161 2162 env_from_flist 2163 2164 if [[ -z $CODEMGR_WS ]]; then 2165 print -u2 "SCM not detected/supported and CODEMGR_WS not specified" 2166 exit 1 2167 fi 2168 2169 if [[ -z $CODEMGR_PARENT ]]; then 2170 print -u2 "SCM not detected/supported and CODEMGR_PARENT not specified" 2171 exit 1 2172 fi 2173 2174 CWS=$CODEMGR_WS 2175 PWS=$CODEMGR_PARENT 2176fi 2177 2178# 2179# If the user didn't specify a -i option, check to see if there is a 2180# webrev-info file in the workspace directory. 2181# 2182if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then 2183 iflag=1 2184 INCLUDE_FILE="$CWS/webrev-info" 2185fi 2186 2187if [[ -n $iflag ]]; then 2188 if [[ ! -r $INCLUDE_FILE ]]; then 2189 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \ 2190 "not readable." 2191 exit 1 2192 else 2193 # 2194 # $INCLUDE_FILE may be a relative path, and the script alters 2195 # PWD, so we just stash a copy in /tmp. 2196 # 2197 cp $INCLUDE_FILE /tmp/$$.include 2198 fi 2199fi 2200 2201# 2202# Output directory. 2203# 2204WDIR=${WDIR:-$CWS/webrev} 2205 2206# 2207# Name of the webrev, derived from the workspace name; in the 2208# future this could potentially be an option. 2209# 2210WNAME=${CWS##*/} 2211 2212if [ "${WDIR%%/*}" ]; then 2213 WDIR=$PWD/$WDIR 2214fi 2215 2216if [[ ! -d $WDIR ]]; then 2217 mkdir -p $WDIR 2218 [[ $? != 0 ]] && exit 1 2219fi 2220 2221# 2222# Summarize what we're going to do. 2223# 2224if [[ -n $CWS_REV ]]; then 2225 print " Workspace: $CWS (at $CWS_REV)" 2226else 2227 print " Workspace: $CWS" 2228fi 2229if [[ -n $parent_webrev ]]; then 2230 print "Compare against: webrev at $parent_webrev" 2231else 2232 if [[ -n $HG_PARENT ]]; then 2233 hg_parent_short=`echo $HG_PARENT \ 2234 | sed -e 's/\([0-9a-f]\{12\}\).*/\1/'` 2235 print "Compare against: $PWS (at $hg_parent_short)" 2236 else 2237 print "Compare against: $PWS" 2238 fi 2239fi 2240 2241[[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE" 2242print " Output to: $WDIR" 2243 2244# 2245# Save the file list in the webrev dir 2246# 2247[[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list 2248 2249# 2250# Bug IDs will be replaced by a URL. Order of precedence 2251# is: default location, $WEBREV_BUGURL, the -O flag. 2252# 2253BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr=' 2254[[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL" 2255[[ -n "$Oflag" ]] && \ 2256 BUGURL='http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=' 2257 2258# 2259# Likewise, ARC cases will be replaced by a URL. Order of precedence 2260# is: default, $WEBREV_SACURL, the -O flag. 2261# 2262# Note that -O also triggers different substitution behavior for 2263# SACURL. See sac2url(). 2264# 2265SACURL='http://sac.eng.sun.com' 2266[[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL" 2267[[ -n "$Oflag" ]] && \ 2268 SACURL='http://www.opensolaris.org/os/community/arc/caselog' 2269 2270rm -f $WDIR/$WNAME.patch 2271rm -f $WDIR/$WNAME.ps 2272rm -f $WDIR/$WNAME.pdf 2273 2274touch $WDIR/$WNAME.patch 2275 2276print " Output Files:" 2277 2278# 2279# Clean up the file list: Remove comments, blank lines and env variables. 2280# 2281sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean 2282FLIST=/tmp/$$.flist.clean 2283 2284# 2285# For Mercurial, create a cache of manifest entries. 2286# 2287if [[ $SCM_MODE == "mercurial" ]]; then 2288 # 2289 # Transform the FLIST into a temporary sed script that matches 2290 # relevant entries in the Mercurial manifest as follows: 2291 # 1) The script will be used against the parent revision manifest, 2292 # so for FLIST lines that have two filenames (a renamed file) 2293 # keep only the old name. 2294 # 2) Escape all forward slashes the filename. 2295 # 3) Change the filename into another sed command that matches 2296 # that file in "hg manifest -v" output: start of line, three 2297 # octal digits for file permissions, space, a file type flag 2298 # character, space, the filename, end of line. 2299 # 2300 SEDFILE=/tmp/$$.manifest.sed 2301 sed ' 2302 s#^[^ ]* ## 2303 s#/#\\\/#g 2304 s#^.*$#/^... . &$/p# 2305 ' < $FLIST > $SEDFILE 2306 2307 # 2308 # Apply the generated script to the output of "hg manifest -v" 2309 # to get the relevant subset for this webrev. 2310 # 2311 HG_PARENT_MANIFEST=/tmp/$$.manifest 2312 hg -R $CWS manifest -v -r $HG_PARENT | 2313 sed -n -f $SEDFILE > $HG_PARENT_MANIFEST 2314fi 2315 2316# 2317# First pass through the files: generate the per-file webrev HTML-files. 2318# 2319cat $FLIST | while read LINE 2320do 2321 set - $LINE 2322 P=$1 2323 2324 # 2325 # Normally, each line in the file list is just a pathname of a 2326 # file that has been modified or created in the child. A file 2327 # that is renamed in the child workspace has two names on the 2328 # line: new name followed by the old name. 2329 # 2330 oldname="" 2331 oldpath="" 2332 rename= 2333 if [[ $# -eq 2 ]]; then 2334 PP=$2 # old filename 2335 oldname=" (was $PP)" 2336 oldpath="$PP" 2337 rename=1 2338 PDIR=${PP%/*} 2339 if [[ $PDIR == $PP ]]; then 2340 PDIR="." # File at root of workspace 2341 fi 2342 2343 PF=${PP##*/} 2344 2345 DIR=${P%/*} 2346 if [[ $DIR == $P ]]; then 2347 DIR="." # File at root of workspace 2348 fi 2349 2350 F=${P##*/} 2351 2352 else 2353 DIR=${P%/*} 2354 if [[ "$DIR" == "$P" ]]; then 2355 DIR="." # File at root of workspace 2356 fi 2357 2358 F=${P##*/} 2359 2360 PP=$P 2361 PDIR=$DIR 2362 PF=$F 2363 fi 2364 2365 COMM=`getcomments html $P $PP` 2366 2367 print "\t$P$oldname\n\t\t\c" 2368 2369 # Make the webrev mirror directory if necessary 2370 mkdir -p $WDIR/$DIR 2371 2372 # 2373 # If we're in OpenSolaris mode, we enforce a minor policy: 2374 # help to make sure the reviewer doesn't accidentally publish 2375 # source which is in usr/closed/* or deleted_files/usr/closed/* 2376 # 2377 if [[ -n "$Oflag" ]]; then 2378 pclosed=${P##usr/closed/} 2379 pdeleted=${P##deleted_files/usr/closed/} 2380 if [[ "$pclosed" != "$P" || "$pdeleted" != "$P" ]]; then 2381 print "*** Omitting closed source for OpenSolaris" \ 2382 "mode review" 2383 continue 2384 fi 2385 fi 2386 2387 # 2388 # We stash old and new files into parallel directories in $WDIR 2389 # and do our diffs there. This makes it possible to generate 2390 # clean looking diffs which don't have absolute paths present. 2391 # 2392 2393 build_old_new "$WDIR" "$PWS" "$PDIR" "$PF" "$CWS" "$DIR" "$F" || \ 2394 continue 2395 2396 # 2397 # Keep the old PWD around, so we can safely switch back after 2398 # diff generation, such that build_old_new runs in a 2399 # consistent environment. 2400 # 2401 OWD=$PWD 2402 cd $WDIR/raw_files 2403 ofile=old/$PDIR/$PF 2404 nfile=new/$DIR/$F 2405 2406 mv_but_nodiff= 2407 cmp $ofile $nfile > /dev/null 2>&1 2408 if [[ $? == 0 && $rename == 1 ]]; then 2409 mv_but_nodiff=1 2410 fi 2411 2412 # 2413 # If we have old and new versions of the file then run the appropriate 2414 # diffs. This is complicated by a couple of factors: 2415 # 2416 # - renames must be handled specially: we emit a 'remove' 2417 # diff and an 'add' diff 2418 # - new files and deleted files must be handled specially 2419 # - Solaris patch(1m) can't cope with file creation 2420 # (and hence renames) as of this writing. 2421 # - To make matters worse, gnu patch doesn't interpret the 2422 # output of Solaris diff properly when it comes to 2423 # adds and deletes. We need to do some "cleansing" 2424 # transformations: 2425 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ 2426 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ 2427 # 2428 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" 2429 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" 2430 2431 rm -f $WDIR/$DIR/$F.patch 2432 if [[ -z $rename ]]; then 2433 if [ ! -f "$ofile" ]; then 2434 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2435 > $WDIR/$DIR/$F.patch 2436 elif [ ! -f "$nfile" ]; then 2437 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2438 > $WDIR/$DIR/$F.patch 2439 else 2440 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch 2441 fi 2442 else 2443 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2444 > $WDIR/$DIR/$F.patch 2445 2446 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2447 >> $WDIR/$DIR/$F.patch 2448 2449 fi 2450 2451 # 2452 # Tack the patch we just made onto the accumulated patch for the 2453 # whole wad. 2454 # 2455 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch 2456 2457 print " patch\c" 2458 2459 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2460 2461 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff 2462 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2463 > $WDIR/$DIR/$F.cdiff.html 2464 print " cdiffs\c" 2465 2466 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff 2467 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2468 > $WDIR/$DIR/$F.udiff.html 2469 2470 print " udiffs\c" 2471 2472 if [[ -x $WDIFF ]]; then 2473 $WDIFF -c "$COMM" \ 2474 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ 2475 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2476 if [[ $? -eq 0 ]]; then 2477 print " wdiffs\c" 2478 else 2479 print " wdiffs[fail]\c" 2480 fi 2481 fi 2482 2483 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2484 > $WDIR/$DIR/$F.sdiff.html 2485 print " sdiffs\c" 2486 2487 print " frames\c" 2488 2489 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2490 2491 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2492 2493 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2494 # renamed file: may also have differences 2495 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2496 elif [[ -f $nfile ]]; then 2497 # new file: count added lines 2498 difflines /dev/null $nfile > $WDIR/$DIR/$F.count 2499 elif [[ -f $ofile ]]; then 2500 # old file: count deleted lines 2501 difflines $ofile /dev/null > $WDIR/$DIR/$F.count 2502 fi 2503 2504 # 2505 # Now we generate the postscript for this file. We generate diffs 2506 # only in the event that there is delta, or the file is new (it seems 2507 # tree-killing to print out the contents of deleted files). 2508 # 2509 if [[ -f $nfile ]]; then 2510 ocr=$ofile 2511 [[ ! -f $ofile ]] && ocr=/dev/null 2512 2513 if [[ -z $mv_but_nodiff ]]; then 2514 textcomm=`getcomments text $P $PP` 2515 if [[ -x $CODEREVIEW ]]; then 2516 $CODEREVIEW -y "$textcomm" \ 2517 -e $ocr $nfile \ 2518 > /tmp/$$.psfile 2>/dev/null && 2519 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps 2520 if [[ $? -eq 0 ]]; then 2521 print " ps\c" 2522 else 2523 print " ps[fail]\c" 2524 fi 2525 fi 2526 fi 2527 fi 2528 2529 if [[ -f $ofile ]]; then 2530 source_to_html Old $PP < $ofile > $WDIR/$DIR/$F-.html 2531 print " old\c" 2532 fi 2533 2534 if [[ -f $nfile ]]; then 2535 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html 2536 print " new\c" 2537 fi 2538 2539 cd $OWD 2540 2541 print 2542done 2543 2544frame_nav_js > $WDIR/ancnav.js 2545frame_navigation > $WDIR/ancnav.html 2546 2547if [[ ! -f $WDIR/$WNAME.ps ]]; then 2548 print " Generating PDF: Skipped: no output available" 2549elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then 2550 print " Generating PDF: \c" 2551 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf 2552 print "Done." 2553else 2554 print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'" 2555fi 2556 2557# If we're in OpenSolaris mode and there's a closed dir under $WDIR, 2558# delete it - prevent accidental publishing of closed source 2559 2560if [[ -n "$Oflag" ]]; then 2561 /usr/bin/find $WDIR -type d -name closed -exec /bin/rm -rf {} \; 2562fi 2563 2564# Now build the index.html file that contains 2565# links to the source files and their diffs. 2566 2567cd $CWS 2568 2569# Save total changed lines for Code Inspection. 2570print "$TOTL" > $WDIR/TotalChangedLines 2571 2572print " index.html: \c" 2573INDEXFILE=$WDIR/index.html 2574exec 3<&1 # duplicate stdout to FD3. 2575exec 1<&- # Close stdout. 2576exec > $INDEXFILE # Open stdout to index file. 2577 2578print "$HTML<head>$STDHEAD" 2579print "<title>$WNAME</title>" 2580print "</head>" 2581print "<body id=\"SUNWwebrev\">" 2582print "<div class=\"summary\">" 2583print "<h2>Code Review for $WNAME</h2>" 2584 2585print "<table>" 2586 2587# 2588# Get the preparer's name: 2589# 2590# If the SCM detected is Mercurial, and the configuration property 2591# ui.username is available, use that, but be careful to properly escape 2592# angle brackets (HTML syntax characters) in the email address. 2593# 2594# Otherwise, use the current userid in the form "John Doe (jdoe)", but 2595# to maintain compatibility with passwd(4), we must support '&' substitutions. 2596# 2597preparer= 2598if [[ "$SCM_MODE" == mercurial ]]; then 2599 preparer=`hg showconfig ui.username 2>/dev/null` 2600 if [[ -n "$preparer" ]]; then 2601 preparer="$(echo "$preparer" | html_quote)" 2602 fi 2603fi 2604if [[ -z "$preparer" ]]; then 2605 preparer=$( 2606 $PERL -e ' 2607 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<); 2608 if ($login) { 2609 $gcos =~ s/\&/ucfirst($login)/e; 2610 printf "%s (%s)\n", $gcos, $login; 2611 } else { 2612 printf "(unknown)\n"; 2613 } 2614 ') 2615fi 2616 2617print "<tr><th>Prepared by:</th><td>$preparer on `date`</td></tr>" 2618print "<tr><th>Workspace:</th><td>$CWS" 2619if [[ -n $CWS_REV ]]; then 2620 print "(at $CWS_REV)" 2621fi 2622print "</td></tr>" 2623print "<tr><th>Compare against:</th><td>" 2624if [[ -n $parent_webrev ]]; then 2625 print "webrev at $parent_webrev" 2626else 2627 print "$PWS" 2628 if [[ -n $hg_parent_short ]]; then 2629 print "(at $hg_parent_short)" 2630 fi 2631fi 2632print "</td></tr>" 2633print "<tr><th>Summary of changes:</th><td>" 2634printCI $TOTL $TINS $TDEL $TMOD $TUNC 2635print "</td></tr>" 2636 2637if [[ -f $WDIR/$WNAME.patch ]]; then 2638 print "<tr><th>Patch of changes:</th><td>" 2639 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>" 2640fi 2641if [[ -f $WDIR/$WNAME.pdf ]]; then 2642 print "<tr><th>Printable review:</th><td>" 2643 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>" 2644fi 2645 2646if [[ -n "$iflag" ]]; then 2647 print "<tr><th>Author comments:</th><td><div>" 2648 cat /tmp/$$.include 2649 print "</div></td></tr>" 2650fi 2651print "</table>" 2652print "</div>" 2653 2654# 2655# Second pass through the files: generate the rest of the index file 2656# 2657cat $FLIST | while read LINE 2658do 2659 set - $LINE 2660 P=$1 2661 2662 if [[ $# == 2 ]]; then 2663 PP=$2 2664 oldname="$PP" 2665 else 2666 PP=$P 2667 oldname="" 2668 fi 2669 2670 mv_but_nodiff= 2671 cmp $WDIR/raw_files/old/$PP $WDIR/raw_files/new/$P > /dev/null 2>&1 2672 if [[ $? == 0 && -n "$oldname" ]]; then 2673 mv_but_nodiff=1 2674 fi 2675 2676 DIR=${P%/*} 2677 if [[ $DIR == $P ]]; then 2678 DIR="." # File at root of workspace 2679 fi 2680 2681 # Avoid processing the same file twice. 2682 # It's possible for renamed files to 2683 # appear twice in the file list 2684 2685 F=$WDIR/$P 2686 2687 print "<p>" 2688 2689 # If there's a diffs file, make diffs links 2690 2691 if [[ -f $F.cdiff.html ]]; then 2692 print "<a href=\"$P.cdiff.html\">Cdiffs</a>" 2693 print "<a href=\"$P.udiff.html\">Udiffs</a>" 2694 2695 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 2696 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 2697 fi 2698 2699 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 2700 2701 print "<a href=\"$P.frames.html\">Frames</a>" 2702 else 2703 print " ------ ------ ------" 2704 2705 if [[ -x $WDIFF ]]; then 2706 print " ------" 2707 fi 2708 2709 print " ------" 2710 fi 2711 2712 # If there's an old file, make the link 2713 2714 if [[ -f $F-.html ]]; then 2715 print "<a href=\"$P-.html\">Old</a>" 2716 else 2717 print " ---" 2718 fi 2719 2720 # If there's an new file, make the link 2721 2722 if [[ -f $F.html ]]; then 2723 print "<a href=\"$P.html\">New</a>" 2724 else 2725 print " ---" 2726 fi 2727 2728 if [[ -f $F.patch ]]; then 2729 print "<a href=\"$P.patch\">Patch</a>" 2730 else 2731 print " -----" 2732 fi 2733 2734 if [[ -f $WDIR/raw_files/new/$P ]]; then 2735 print "<a href=\"raw_files/new/$P\">Raw</a>" 2736 else 2737 print " ---" 2738 fi 2739 2740 print "<b>$P</b>" 2741 2742 # For renamed files, clearly state whether or not they are modified 2743 if [[ -n "$oldname" ]]; then 2744 if [[ -n "$mv_but_nodiff" ]]; then 2745 print "<i>(renamed only, was $oldname)</i>" 2746 else 2747 print "<i>(modified and renamed, was $oldname)</i>" 2748 fi 2749 fi 2750 2751 # If there's an old file, but no new file, the file was deleted 2752 if [[ -f $F-.html && ! -f $F.html ]]; then 2753 print " <i>(deleted)</i>" 2754 fi 2755 2756 # 2757 # Check for usr/closed and deleted_files/usr/closed 2758 # 2759 if [ ! -z "$Oflag" ]; then 2760 if [[ $P == usr/closed/* || \ 2761 $P == deleted_files/usr/closed/* ]]; then 2762 print " <i>Closed source: omitted from" \ 2763 "this review</i>" 2764 fi 2765 fi 2766 2767 print "</p>" 2768 # Insert delta comments 2769 2770 print "<blockquote><pre>" 2771 getcomments html $P $PP 2772 print "</pre>" 2773 2774 # Add additional comments comment 2775 2776 print "<!-- Add comments to explain changes in $P here -->" 2777 2778 # Add count of changes. 2779 2780 if [[ -f $F.count ]]; then 2781 cat $F.count 2782 rm $F.count 2783 fi 2784 2785 if [[ $SCM_MODE == "teamware" || 2786 $SCM_MODE == "mercurial" || 2787 $SCM_MODE == "unknown" ]]; then 2788 2789 # Include warnings for important file mode situations: 2790 # 1) New executable files 2791 # 2) Permission changes of any kind 2792 # 3) Existing executable files 2793 2794 old_mode= 2795 if [[ -f $WDIR/raw_files/old/$PP ]]; then 2796 old_mode=`get_file_mode $WDIR/raw_files/old/$PP` 2797 fi 2798 2799 new_mode= 2800 if [[ -f $WDIR/raw_files/new/$P ]]; then 2801 new_mode=`get_file_mode $WDIR/raw_files/new/$P` 2802 fi 2803 2804 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then 2805 print "<span class=\"chmod\">" 2806 print "<p>new executable file: mode $new_mode</p>" 2807 print "</span>" 2808 elif [[ -n "$old_mode" && -n "$new_mode" && 2809 "$old_mode" != "$new_mode" ]]; then 2810 print "<span class=\"chmod\">" 2811 print "<p>mode change: $old_mode to $new_mode</p>" 2812 print "</span>" 2813 elif [[ "$new_mode" = *[1357]* ]]; then 2814 print "<span class=\"chmod\">" 2815 print "<p>executable file: mode $new_mode</p>" 2816 print "</span>" 2817 fi 2818 fi 2819 2820 print "</blockquote>" 2821done 2822 2823print 2824print 2825print "<hr></hr>" 2826print "<p style=\"font-size: small\">" 2827print "This code review page was prepared using <b>$0</b>" 2828print "(vers $WEBREV_UPDATED)." 2829print "Webrev is maintained by the <a href=\"http://www.opensolaris.org\">" 2830print "OpenSolaris</a> project. The latest version may be obtained" 2831print "<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>" 2832print "</body>" 2833print "</html>" 2834 2835exec 1<&- # Close FD 1. 2836exec 1<&3 # dup FD 3 to restore stdout. 2837exec 3<&- # close FD 3. 2838 2839print "Done." 2840