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 2007 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 1422function look_for_prog 1423{ 1424 typeset path 1425 typeset ppath 1426 typeset progname=$1 1427 1428 ppath=$PATH 1429 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin 1430 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin 1431 ppath=$ppath:/opt/onbld/bin/`/usr/bin/uname -p` 1432 1433 PATH=$ppath prog=`whence $progname` 1434 if [[ -n $prog ]]; then 1435 print $prog 1436 fi 1437} 1438 1439# 1440# Usage message. 1441# 1442function usage 1443{ 1444 print 'Usage:\twebrev [common-options] 1445 webrev [common-options] ( <file> | - ) 1446 webrev [common-options] -w <wx file> 1447 webrev [common-options] -l [arguments to 'putback'] 1448 1449Options: 1450 -O: Print bugids/arc cases suitable for OpenSolaris. 1451 -i <filename>: Include <filename> in the index.html file. 1452 -o <outdir>: Output webrev to specified directory. 1453 -p <compare-against>: Use specified parent wkspc or basis for comparison 1454 -w <wxfile>: Use specified wx active file. 1455 1456Environment: 1457 WDIR: Control the output directory. 1458 WEBREV_BUGURL: Control the URL prefix for bugids. 1459 WEBREV_SACURL: Control the URL prefix for ARC cases. 1460 1461SCM Environment: 1462 Teamware: CODEMGR_WS: Workspace location. 1463 Teamware: CODEMGR_PARENT: Parent workspace location. 1464' 1465 1466 exit 2 1467} 1468 1469# 1470# 1471# Main program starts here 1472# 1473# 1474 1475trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15 1476 1477set +o noclobber 1478 1479[[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` 1480[[ -z $WX ]] && WX=`look_for_prog wx` 1481[[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` 1482[[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf` 1483[[ -z $PERL ]] && PERL=`look_for_prog perl` 1484 1485if [[ ! -x $PERL ]]; then 1486 print -u2 "Error: No perl interpreter found. Exiting." 1487 exit 1 1488fi 1489 1490# 1491# These aren't fatal, but we want to note them to the user. 1492# We don't warn on the absence of 'wx' until later when we've 1493# determined that we actually need to try to invoke it. 1494# 1495[[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found." 1496[[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found." 1497[[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found." 1498 1499# Declare global total counters. 1500integer TOTL TINS TDEL TMOD TUNC 1501 1502flist_mode= 1503flist_file= 1504iflag= 1505oflag= 1506pflag= 1507lflag= 1508wflag= 1509Oflag= 1510while getopts "i:o:p:lwO" opt 1511do 1512 case $opt in 1513 i) iflag=1 1514 INCLUDE_FILE=$OPTARG;; 1515 1516 o) oflag=1 1517 WDIR=$OPTARG;; 1518 1519 p) pflag=1 1520 codemgr_parent=$OPTARG;; 1521 1522 # 1523 # If -l has been specified, we need to abort further options 1524 # processing, because subsequent arguments are going to be 1525 # arguments to 'putback -n'. 1526 # 1527 l) lflag=1 1528 break;; 1529 1530 w) wflag=1;; 1531 1532 O) Oflag=1;; 1533 1534 ?) usage;; 1535 esac 1536done 1537 1538FLIST=/tmp/$$.flist 1539 1540if [[ -n $wflag && -n $lflag ]]; then 1541 usage 1542fi 1543 1544# 1545# If this manually set as the parent, and it appears to be an earlier webrev, 1546# then note that fact and set the parent to the raw_files/new subdirectory. 1547# 1548if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then 1549 parent_webrev="$codemgr_parent" 1550 codemgr_parent="$codemgr_parent/raw_files/new" 1551fi 1552 1553if [[ -z $wflag && -z $lflag ]]; then 1554 shift $(($OPTIND - 1)) 1555 1556 if [[ $1 == "-" ]]; then 1557 cat > $FLIST 1558 flist_mode="stdin" 1559 flist_done=1 1560 shift 1561 elif [[ -n $1 ]]; then 1562 if [[ ! -r $1 ]]; then 1563 print -u2 "$1: no such file or not readable" 1564 usage 1565 fi 1566 cat $1 > $FLIST 1567 flist_mode="file" 1568 flist_file=$1 1569 flist_done=1 1570 shift 1571 else 1572 flist_mode="auto" 1573 fi 1574fi 1575 1576# 1577# Before we go on to further consider -l and -w, work out which SCM we think 1578# is in use. 1579# 1580SCM_MODE=`detect_scm $FLIST` 1581if [[ $SCM_MODE == "unknown" ]]; then 1582 print -u2 "Unable to determine SCM type currently in use." 1583 print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in" 1584 print -u2 " the environment or in the file list." 1585 exit 1 1586fi 1587 1588print -u2 " SCM detected: $SCM_MODE" 1589 1590if [[ -n $lflag ]]; then 1591 # 1592 # If the -l flag is given instead of the name of a file list, 1593 # then generate the file list by extracting file names from a 1594 # putback -n. 1595 # 1596 shift $(($OPTIND - 1)) 1597 flist_from_teamware "$*" 1598 flist_done=1 1599 shift $# 1600 1601elif [[ -n $wflag ]]; then 1602 # 1603 # If the -w is given then assume the file list is in Bonwick's "wx" 1604 # command format, i.e. pathname lines alternating with SCCS comment 1605 # lines with blank lines as separators. Use the SCCS comments later 1606 # in building the index.html file. 1607 # 1608 shift $(($OPTIND - 1)) 1609 wxfile=$1 1610 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then 1611 if [[ -r $CODEMGR_WS/wx/active ]]; then 1612 wxfile=$CODEMGR_WS/wx/active 1613 fi 1614 fi 1615 1616 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \ 1617 "be auto-detected (check \$CODEMGR_WS)" && exit 1 1618 1619 print -u2 " File list from: wx 'active' file '$wxfile' ... \c" 1620 flist_from_wx $wxfile 1621 flist_done=1 1622 if [[ -n "$*" ]]; then 1623 shift 1624 fi 1625elif [[ $flist_mode == "stdin" ]]; then 1626 print -u2 " File list from: standard input" 1627elif [[ $flist_mode == "file" ]]; then 1628 print -u2 " File list from: $flist_file" 1629fi 1630 1631if [[ $# -gt 0 ]]; then 1632 print -u2 "WARNING: unused arguments: $*" 1633fi 1634 1635if [[ $SCM_MODE == "teamware" ]]; then 1636 # 1637 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can 1638 # be set in a number of ways, in decreasing precedence: 1639 # 1640 # 1) on the command line (only for the parent) 1641 # 2) in the user environment 1642 # 3) in the flist 1643 # 4) automatically based on the workspace (only for the parent) 1644 # 1645 1646 # 1647 # Here is case (2): the user environment 1648 # 1649 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1650 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then 1651 print -u2 "$codemgr_ws: no such workspace" 1652 exit 1 1653 fi 1654 1655 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 1656 codemgr_parent=$CODEMGR_PARENT 1657 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then 1658 print -u2 "$codemgr_parent: no such directory" 1659 exit 1 1660 fi 1661 1662 # 1663 # If we're in auto-detect mode and we haven't already gotten the file 1664 # list, then see if we can get it by probing for wx. 1665 # 1666 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then 1667 if [[ ! -x $WX ]]; then 1668 print -u2 "WARNING: wx not found!" 1669 fi 1670 1671 # 1672 # We need to use wx list -w so that we get renamed files, etc. 1673 # but only if a wx active file exists-- otherwise wx will 1674 # hang asking us to initialize our wx information. 1675 # 1676 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then 1677 print -u2 " File list from: 'wx list -w' ... \c" 1678 $WX list -w > $FLIST 1679 $WX comments > /tmp/$$.wx_comments 1680 wxfile=/tmp/$$.wx_comments 1681 print -u2 "done" 1682 flist_done=1 1683 fi 1684 fi 1685 1686 # 1687 # If by hook or by crook we've gotten a file list by now (perhaps 1688 # from the command line), eval it to extract environment variables from 1689 # it: This is step (3). 1690 # 1691 env_from_flist 1692 1693 # 1694 # Continuing step (3): If we still have no file list, we'll try to get 1695 # it from teamware. 1696 # 1697 if [[ -z $flist_done ]]; then 1698 flist_from_teamware 1699 env_from_flist 1700 fi 1701 1702 # 1703 # Observe true directory name of CODEMGR_WS, as used later in 1704 # webrev title. 1705 # 1706 codemgr_ws=$(cd $codemgr_ws;print $PWD) 1707 1708 # 1709 # (4) If we still don't have a value for codemgr_parent, get it 1710 # from workspace. 1711 # 1712 [[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent` 1713 if [[ ! -d $codemgr_parent ]]; then 1714 print -u2 "$CODEMGR_PARENT: no such parent workspace" 1715 exit 1 1716 fi 1717 1718 # 1719 # Reset CODEMGR_WS to make sure teamware commands are happy. 1720 # 1721 CODEMGR_WS=$codemgr_ws 1722 CWS=$codemgr_ws 1723 PWS=$codemgr_parent 1724fi 1725 1726# 1727# If the user didn't specify a -i option, check to see if there is a 1728# webrev-info file in the workspace directory. 1729# 1730if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then 1731 iflag=1 1732 INCLUDE_FILE="$CWS/webrev-info" 1733fi 1734 1735if [[ -n $iflag ]]; then 1736 if [[ ! -r $INCLUDE_FILE ]]; then 1737 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \ 1738 "not readable." 1739 exit 1 1740 else 1741 # 1742 # $INCLUDE_FILE may be a relative path, and the script alters 1743 # PWD, so we just stash a copy in /tmp. 1744 # 1745 cp $INCLUDE_FILE /tmp/$$.include 1746 fi 1747fi 1748 1749# 1750# Output directory. 1751# 1752WDIR=${WDIR:-$CWS/webrev} 1753 1754# 1755# Name of the webrev, derived from the workspace name; in the 1756# future this could potentially be an option. 1757# 1758WNAME=${CWS##*/} 1759 1760if [ "${WDIR%%/*}" ]; then 1761 WDIR=$PWD/$WDIR 1762fi 1763 1764if [[ ! -d $WDIR ]]; then 1765 mkdir -p $WDIR 1766 [[ $? != 0 ]] && exit 1 1767fi 1768 1769# 1770# Summarize what we're going to do. 1771# 1772print " Workspace: $CWS" 1773if [[ -n $parent_webrev ]]; then 1774 print "Compare against: webrev at $parent_webrev" 1775else 1776 print "Compare against: $PWS" 1777fi 1778 1779[[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE" 1780print " Output to: $WDIR" 1781 1782# 1783# Save the file list in the webrev dir 1784# 1785[[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list 1786 1787# 1788# Bug IDs will be replaced by a URL. Order of precedence 1789# is: default location, $WEBREV_BUGURL, the -O flag. 1790# 1791BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr=' 1792[[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL" 1793[[ -n "$Oflag" ]] && \ 1794 BUGURL='http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=' 1795 1796# 1797# Likewise, ARC cases will be replaced by a URL. Order of precedence 1798# is: default, $WEBREV_SACURL, the -O flag. 1799# 1800# Note that -O also triggers different substitution behavior for 1801# SACURL. See sac2url(). 1802# 1803SACURL='http://sac.eng.sun.com' 1804[[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL" 1805[[ -n "$Oflag" ]] && \ 1806 SACURL='http://www.opensolaris.org/os/community/arc/caselog' 1807 1808rm -f $WDIR/$WNAME.patch 1809rm -f $WDIR/$WNAME.ps 1810rm -f $WDIR/$WNAME.pdf 1811 1812touch $WDIR/$WNAME.patch 1813 1814print " Output Files:" 1815 1816# 1817# Clean up the file list: Remove comments, blank lines and env variables. 1818# 1819sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean 1820FLIST=/tmp/$$.flist.clean 1821 1822# 1823# First pass through the files: generate the per-file webrev HTML-files. 1824# 1825cat $FLIST | while read LINE 1826do 1827 set - $LINE 1828 P=$1 1829 1830 # 1831 # Normally, each line in the file list is just a pathname of a 1832 # file that has been modified or created in the child. A file 1833 # that is renamed in the child workspace has two names on the 1834 # line: new name followed by the old name. 1835 # 1836 oldname="" 1837 oldpath="" 1838 rename= 1839 if [[ $# -eq 2 ]]; then 1840 PP=$2 # old filename 1841 oldname=" (was $PP)" 1842 oldpath="$PP" 1843 rename=1 1844 PDIR=${PP%/*} 1845 if [[ $PDIR == $PP ]]; then 1846 PDIR="." # File at root of workspace 1847 fi 1848 1849 PF=${PP##*/} 1850 1851 DIR=${P%/*} 1852 if [[ $DIR == $P ]]; then 1853 DIR="." # File at root of workspace 1854 fi 1855 1856 F=${P##*/} 1857 1858 else 1859 DIR=${P%/*} 1860 if [[ "$DIR" == "$P" ]]; then 1861 DIR="." # File at root of workspace 1862 fi 1863 1864 F=${P##*/} 1865 1866 PP=$P 1867 PDIR=$DIR 1868 PF=$F 1869 fi 1870 1871 COMM=`getcomments html $P $PP` 1872 1873 if [[ ! -d $CWS/$DIR ]]; then 1874 print " $CWS/$DIR: no such directory" 1875 continue 1876 fi 1877 1878 print "\t$P$oldname\n\t\t\c" 1879 1880 # Make the webrev mirror directory if necessary 1881 mkdir -p $WDIR/$DIR 1882 1883 # cd to the directory so the names are short 1884 cd $CWS/$DIR 1885 1886 # 1887 # If we're in OpenSolaris mode, we enforce a minor policy: 1888 # help to make sure the reviewer doesn't accidentally publish 1889 # source which is in usr/closed/* or deleted_files/usr/closed/* 1890 # 1891 if [[ -n "$Oflag" ]]; then 1892 pclosed=${P##usr/closed/} 1893 pdeleted=${P##deleted_files/usr/closed/} 1894 if [[ "$pclosed" != "$P" || "$pdeleted" != "$P" ]]; then 1895 print "*** Omitting closed source for OpenSolaris" \ 1896 "mode review" 1897 continue 1898 fi 1899 fi 1900 1901 # 1902 # We stash old and new files into parallel directories in /tmp 1903 # and do our diffs there. This makes it possible to generate 1904 # clean looking diffs which don't have absolute paths present. 1905 # 1906 olddir=$WDIR/raw_files/old 1907 newdir=$WDIR/raw_files/new 1908 mkdir -p $olddir 1909 mkdir -p $newdir 1910 mkdir -p $olddir/$PDIR 1911 mkdir -p $newdir/$DIR 1912 1913 if [[ $SCM_MODE == "teamware" ]]; then 1914 # If the child's version doesn't exist then 1915 # get a readonly copy. 1916 1917 if [[ ! -f $F && -f SCCS/s.$F ]]; then 1918 sccs get -s $F 1919 fi 1920 1921 # 1922 # Snag new version of file. 1923 # 1924 rm -f $newdir/$DIR/$F 1925 cp $F $newdir/$DIR/$F 1926 1927 # 1928 # Get the parent's version of the file. First see whether the 1929 # child's version is checked out and get the parent's version 1930 # with keywords expanded or unexpanded as appropriate. 1931 # 1932 if [ -f "$PWS/$PDIR/SCCS/s.$PF" -o \ 1933 -f "$PWS/$PDIR/SCCS/p.$PF" ]; then 1934 rm -f $olddir/$PDIR/$PF 1935 if [ -f "SCCS/p.$F" ]; then 1936 sccs get -s -p -k $PWS/$PDIR/$PF \ 1937 > $olddir/$PDIR/$PF 1938 else 1939 sccs get -s -p $PWS/$PDIR/$PF \ 1940 > $olddir/$PDIR/$PF 1941 fi 1942 else 1943 if [[ -f $PWS/$PDIR/$PF ]]; then 1944 # Parent is not a real workspace, but just a raw 1945 # directory tree - use the file that's there as 1946 # the old file. 1947 1948 rm -f $olddir/$DIR/$F 1949 cp $PWS/$PDIR/$PF $olddir/$DIR/$F 1950 fi 1951 fi 1952 fi 1953 1954 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then 1955 print "*** Error: file not in parent or child" 1956 continue 1957 fi 1958 1959 cd $WDIR/raw_files 1960 ofile=old/$PDIR/$PF 1961 nfile=new/$DIR/$F 1962 1963 mv_but_nodiff= 1964 cmp $ofile $nfile > /dev/null 2>&1 1965 if [[ $? == 0 && $rename == 1 ]]; then 1966 mv_but_nodiff=1 1967 fi 1968 1969 # 1970 # If we have old and new versions of the file then run the appropriate 1971 # diffs. This is complicated by a couple of factors: 1972 # 1973 # - renames must be handled specially: we emit a 'remove' 1974 # diff and an 'add' diff 1975 # - new files and deleted files must be handled specially 1976 # - Solaris patch(1m) can't cope with file creation 1977 # (and hence renames) as of this writing. 1978 # - To make matters worse, gnu patch doesn't interpret the 1979 # output of Solaris diff properly when it comes to 1980 # adds and deletes. We need to do some "cleansing" 1981 # transformations: 1982 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ 1983 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ 1984 # 1985 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" 1986 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" 1987 1988 rm -f $WDIR/$DIR/$F.patch 1989 if [[ -z $rename ]]; then 1990 if [ ! -f "$ofile" ]; then 1991 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 1992 > $WDIR/$DIR/$F.patch 1993 elif [ ! -f "$nfile" ]; then 1994 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 1995 > $WDIR/$DIR/$F.patch 1996 else 1997 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch 1998 fi 1999 else 2000 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2001 > $WDIR/$DIR/$F.patch 2002 2003 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2004 >> $WDIR/$DIR/$F.patch 2005 2006 fi 2007 2008 # 2009 # Tack the patch we just made onto the accumulated patch for the 2010 # whole wad. 2011 # 2012 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch 2013 2014 print " patch\c" 2015 2016 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2017 2018 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff 2019 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2020 > $WDIR/$DIR/$F.cdiff.html 2021 print " cdiffs\c" 2022 2023 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff 2024 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2025 > $WDIR/$DIR/$F.udiff.html 2026 2027 print " udiffs\c" 2028 2029 if [[ -x $WDIFF ]]; then 2030 $WDIFF -c "$COMM" \ 2031 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ 2032 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2033 if [[ $? -eq 0 ]]; then 2034 print " wdiffs\c" 2035 else 2036 print " wdiffs[fail]\c" 2037 fi 2038 fi 2039 2040 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2041 > $WDIR/$DIR/$F.sdiff.html 2042 print " sdiffs\c" 2043 2044 print " frames\c" 2045 2046 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2047 2048 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2049 2050 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2051 # renamed file: may also have differences 2052 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2053 elif [[ -f $nfile ]]; then 2054 # new file: count added lines 2055 difflines /dev/null $nfile > $WDIR/$DIR/$F.count 2056 elif [[ -f $ofile ]]; then 2057 # old file: count deleted lines 2058 difflines $ofile /dev/null > $WDIR/$DIR/$F.count 2059 fi 2060 2061 # 2062 # Now we generate the postscript for this file. We generate diffs 2063 # only in the event that there is delta, or the file is new (it seems 2064 # tree-killing to print out the contents of deleted files). 2065 # 2066 if [[ -f $nfile ]]; then 2067 ocr=$ofile 2068 [[ ! -f $ofile ]] && ocr=/dev/null 2069 2070 if [[ -z $mv_but_nodiff ]]; then 2071 textcomm=`getcomments text $P $PP` 2072 if [[ -x $CODEREVIEW ]]; then 2073 $CODEREVIEW -y "$textcomm" \ 2074 -e $ocr $nfile \ 2075 > /tmp/$$.psfile 2>/dev/null && 2076 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps 2077 if [[ $? -eq 0 ]]; then 2078 print " ps\c" 2079 else 2080 print " ps[fail]\c" 2081 fi 2082 fi 2083 fi 2084 fi 2085 2086 if [[ -f $ofile && -z $mv_but_nodiff ]]; then 2087 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html 2088 print " old\c" 2089 fi 2090 2091 if [[ -f $nfile ]]; then 2092 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html 2093 print " new\c" 2094 fi 2095 2096 print 2097done 2098 2099frame_nav_js > $WDIR/ancnav.js 2100frame_navigation > $WDIR/ancnav.html 2101 2102if [[ ! -f $WDIR/$WNAME.ps ]]; then 2103 print " Generating PDF: Skipped: no output available" 2104elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then 2105 print " Generating PDF: \c" 2106 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf 2107 rm -f $WDIR/$WNAME.ps 2108 print "Done." 2109else 2110 print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'" 2111fi 2112 2113# If we're in OpenSolaris mode and there's a closed dir under $WDIR, 2114# delete it - prevent accidental publishing of closed source 2115 2116if [[ -n "$Oflag" ]]; then 2117 /usr/bin/find $WDIR -type d -name closed -exec /bin/rm -rf {} \; 2118fi 2119 2120# Now build the index.html file that contains 2121# links to the source files and their diffs. 2122 2123cd $CWS 2124 2125# Save total changed lines for Code Inspection. 2126print "$TOTL" > $WDIR/TotalChangedLines 2127 2128print " index.html: \c" 2129INDEXFILE=$WDIR/index.html 2130exec 3<&1 # duplicate stdout to FD3. 2131exec 1<&- # Close stdout. 2132exec > $INDEXFILE # Open stdout to index file. 2133 2134print "$HTML<head>$STDHEAD" 2135print "<title>$WNAME</title>" 2136print "</head>" 2137print "<body id=\"SUNWwebrev\">" 2138print "<div class=\"summary\">" 2139print "<h2>Code Review for $WNAME</h2>" 2140 2141print "<table>" 2142 2143# 2144# Figure out the username and gcos name. To maintain compatibility 2145# with passwd(4), we must support '&' substitutions. 2146# 2147username=`id | cut -d '(' -f 2 | cut -d ')' -f 1` 2148realname=`getent passwd $username | cut -d':' -f 5` 2149userupper=`$PERL -e "print ucfirst $username"` 2150realname=`print $realname | sed s/\&/$userupper/` 2151date="on `date`" 2152 2153if [[ -n "$username" && -n "$realname" ]]; then 2154 print "<tr><th>Prepared by:</th>" 2155 print "<td>$realname ($username) $date</td></tr>" 2156elif [[ -n "$username" ]]; then 2157 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>" 2158fi 2159 2160print "<tr><th>Workspace:</th><td>$CWS</td></tr>" 2161print "<tr><th>Compare against:</th><td>" 2162if [[ -n $parent_webrev ]]; then 2163 print "webrev at $parent_webrev" 2164else 2165 print "$PWS" 2166fi 2167print "</td></tr>" 2168print "<tr><th>Summary of changes:</th><td>" 2169printCI $TOTL $TINS $TDEL $TMOD $TUNC 2170print "</td></tr>" 2171 2172if [[ -f $WDIR/$WNAME.patch ]]; then 2173 print "<tr><th>Patch of changes:</th><td>" 2174 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>" 2175fi 2176if [[ -f $WDIR/$WNAME.pdf ]]; then 2177 print "<tr><th>Printable review:</th><td>" 2178 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>" 2179fi 2180 2181if [[ -n "$iflag" ]]; then 2182 print "<tr><th>Author comments:</th><td><div>" 2183 cat /tmp/$$.include 2184 print "</div></td></tr>" 2185fi 2186print "</table>" 2187print "</div>" 2188 2189 2190# 2191# Second pass through the files: generate the rest of the index file 2192# 2193cat $FLIST | while read LINE 2194do 2195 set - $LINE 2196 P=$1 2197 2198 if [[ $# == 2 ]]; then 2199 PP=$2 2200 oldname=" <i>(was $PP)</i>" 2201 2202 else 2203 PP=$P 2204 oldname="" 2205 fi 2206 2207 DIR=${P%/*} 2208 if [[ $DIR == $P ]]; then 2209 DIR="." # File at root of workspace 2210 fi 2211 2212 # Avoid processing the same file twice. 2213 # It's possible for renamed files to 2214 # appear twice in the file list 2215 2216 F=$WDIR/$P 2217 2218 print "<p>" 2219 2220 # If there's a diffs file, make diffs links 2221 2222 if [[ -f $F.cdiff.html ]]; then 2223 print "<a href=\"$P.cdiff.html\">Cdiffs</a>" 2224 print "<a href=\"$P.udiff.html\">Udiffs</a>" 2225 2226 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 2227 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 2228 fi 2229 2230 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 2231 2232 print "<a href=\"$P.frames.html\">Frames</a>" 2233 else 2234 print " ------ ------ ------" 2235 2236 if [[ -x $WDIFF ]]; then 2237 print " ------" 2238 fi 2239 2240 print " ------" 2241 fi 2242 2243 # If there's an old file, make the link 2244 2245 if [[ -f $F-.html ]]; then 2246 print "<a href=\"$P-.html\">Old</a>" 2247 else 2248 print " ---" 2249 fi 2250 2251 # If there's an new file, make the link 2252 2253 if [[ -f $F.html ]]; then 2254 print "<a href=\"$P.html\">New</a>" 2255 else 2256 print " ---" 2257 fi 2258 2259 if [[ -f $F.patch ]]; then 2260 print "<a href=\"$P.patch\">Patch</a>" 2261 else 2262 print " -----" 2263 fi 2264 2265 if [[ -f $WDIR/raw_files/new/$P ]]; then 2266 print "<a href=\"raw_files/new/$P\">Raw</a>" 2267 else 2268 print " ---" 2269 fi 2270 2271 print "<b>$P</b> $oldname" 2272 2273 # 2274 # Check for usr/closed and deleted_files/usr/closed 2275 # 2276 if [ ! -z "$Oflag" ]; then 2277 if [[ $P == usr/closed/* || \ 2278 $P == deleted_files/usr/closed/* ]]; then 2279 print " <i>Closed source: omitted from" \ 2280 "this review</i>" 2281 fi 2282 fi 2283 2284 print "</p>" 2285 # Insert delta comments 2286 2287 print "<blockquote><pre>" 2288 getcomments html $P $PP 2289 print "</pre>" 2290 2291 # Add additional comments comment 2292 2293 print "<!-- Add comments to explain changes in $P here -->" 2294 2295 # Add count of changes. 2296 2297 if [[ -f $F.count ]]; then 2298 cat $F.count 2299 rm $F.count 2300 fi 2301 print "</blockquote>" 2302done 2303 2304print 2305print 2306print "<hr />" 2307print "<p style=\"font-size: small\">" 2308print "This code review page was prepared using <b>$0</b>" 2309print "(vers $WEBREV_UPDATED)." 2310print "Webrev is maintained by the <a href=\"http://www.opensolaris.org\">" 2311print "OpenSolaris</a> project. The latest version may be obtained" 2312print "<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>" 2313print "</body>" 2314print "</html>" 2315 2316exec 1<&- # Close FD 1. 2317exec 1<&3 # dup FD 3 to restore stdout. 2318exec 3<&- # close FD 3. 2319 2320print "Done." 2321