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