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 2008 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"></meta> 59<meta http-equiv="Pragma" content="no-cache"></meta> 60<meta http-equiv="Expires" content="-1"></meta> 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></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></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"></script> 531 </head> 532 <body id="SUNWwebrev" onkeypress="keypress(event);"> 533 <a name="0"></a> 534 <pre>$comments</pre><hr></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"></frame> 554 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame> 555 </frameset> 556 <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0" 557 marginheight="0" name="nav"></frame> 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 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++; 652 } 653 654 BEGIN { 655 anc=1; 656 inblock=1; 657 printf "<pre>\n"; 658 } 659 NF == 0 || /^<span class=/ { 660 if (inblock == 0) { 661 ia(); 662 inblock=1; 663 } 664 print; 665 next; 666 } 667 { 668 inblock=0; 669 print; 670 } 671 END { 672 ia(); 673 674 printf "<b style=\"font-size: large; color: red\">"; 675 printf "--- EOF ---</b>" 676 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n"; 677 printf "</pre>" 678 printf "<form name=\"eof\">"; 679 printf "<input name=\"value\" value=\"%d\" " \ 680 "type=\"hidden\"></input>", anc - 1; 681 printf "</form>"; 682 } 683 ' $1 684} 685 686 687# 688# relative_dir 689# 690# Print a relative return path from $1 to $2. For example if 691# $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview, 692# this function would print "../../../../". 693# 694# In the event that $1 is not in $2 a warning is printed to stderr, 695# and $2 is returned-- the result of this is that the resulting webrev 696# is not relocatable. 697# 698function relative_dir 699{ 700 typeset cur="${1##$2?(/)}" 701 typeset ret="" 702 if [[ $2 == $cur ]]; then # Should never happen. 703 # Should never happen. 704 print -u2 "\nWARNING: relative_dir: \"$1\" not relative " 705 print -u2 "to \"$2\". Check input paths. Framed webrev " 706 print -u2 "will not be relocatable!" 707 print $2 708 return 709 fi 710 711 while [[ -n ${cur} ]]; 712 do 713 cur=${cur%%*(/)*([!/])} 714 if [[ -z $ret ]]; then 715 ret=".." 716 else 717 ret="../$ret" 718 fi 719 done 720 print $ret 721} 722 723 724# 725# frame_nav_js 726# 727# Emit javascript for frame navigation 728# 729function frame_nav_js 730{ 731cat << \EOF 732var myInt; 733var scrolling=0; 734var sfactor = 3; 735var scount=10; 736 737function scrollByPix() { 738 if (scount<=0) { 739 sfactor*=1.2; 740 scount=10; 741 } 742 parent.lhs.scrollBy(0,sfactor); 743 parent.rhs.scrollBy(0,sfactor); 744 scount--; 745} 746 747function scrollToAnc(num) { 748 749 // Update the value of the anchor in the form which we use as 750 // storage for this value. setAncValue() will take care of 751 // correcting for overflow and underflow of the value and return 752 // us the new value. 753 num = setAncValue(num); 754 755 // Set location and scroll back a little to expose previous 756 // lines. 757 // 758 // Note that this could be improved: it is possible although 759 // complex to compute the x and y position of an anchor, and to 760 // scroll to that location directly. 761 // 762 parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num); 763 parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num); 764 765 parent.lhs.scrollBy(0,-30); 766 parent.rhs.scrollBy(0,-30); 767} 768 769function getAncValue() 770{ 771 return (parseInt(parent.nav.document.diff.real.value)); 772} 773 774function setAncValue(val) 775{ 776 if (val <= 0) { 777 val = 0; 778 parent.nav.document.diff.real.value = val; 779 parent.nav.document.diff.display.value = "BOF"; 780 return (val); 781 } 782 783 // 784 // The way we compute the max anchor value is to stash it 785 // inline in the left and right hand side pages-- it's the same 786 // on each side, so we pluck from the left. 787 // 788 maxval = parent.lhs.document.eof.value.value; 789 if (val < maxval) { 790 parent.nav.document.diff.real.value = val; 791 parent.nav.document.diff.display.value = val.toString(); 792 return (val); 793 } 794 795 // this must be: val >= maxval 796 val = maxval; 797 parent.nav.document.diff.real.value = val; 798 parent.nav.document.diff.display.value = "EOF"; 799 return (val); 800} 801 802function stopScroll() { 803 if (scrolling==1) { 804 clearInterval(myInt); 805 scrolling=0; 806 } 807} 808 809function startScroll() { 810 stopScroll(); 811 scrolling=1; 812 myInt=setInterval("scrollByPix()",10); 813} 814 815function handlePress(b) { 816 817 switch (b) { 818 case 1 : 819 scrollToAnc(-1); 820 break; 821 case 2 : 822 scrollToAnc(getAncValue() - 1); 823 break; 824 case 3 : 825 sfactor=-3; 826 startScroll(); 827 break; 828 case 4 : 829 sfactor=3; 830 startScroll(); 831 break; 832 case 5 : 833 scrollToAnc(getAncValue() + 1); 834 break; 835 case 6 : 836 scrollToAnc(999999); 837 break; 838 } 839} 840 841function handleRelease(b) { 842 stopScroll(); 843} 844 845function keypress(ev) { 846 var keynum; 847 var keychar; 848 849 if (window.event) { // IE 850 keynum = ev.keyCode; 851 } else if (ev.which) { // non-IE 852 keynum = ev.which; 853 } 854 855 keychar = String.fromCharCode(keynum); 856 857 if (keychar == "k") { 858 handlePress(2); 859 return (0); 860 } else if (keychar == "j" || keychar == " ") { 861 handlePress(5); 862 return (0); 863 } 864 return (1); 865} 866 867function ValidateDiffNum(){ 868 val = parent.nav.document.diff.display.value; 869 if (val == "EOF") { 870 scrollToAnc(999999); 871 return; 872 } 873 874 if (val == "BOF") { 875 scrollToAnc(0); 876 return; 877 } 878 879 i=parseInt(val); 880 if (isNaN(i)) { 881 parent.nav.document.diff.display.value = getAncValue(); 882 } else { 883 scrollToAnc(i); 884 } 885 return false; 886} 887 888EOF 889} 890 891# 892# frame_navigation 893# 894# Output anchor navigation file for framed sdiffs. 895# 896function frame_navigation 897{ 898 print "$HTML<head>$STDHEAD" 899 900 cat << \EOF 901<title>Anchor Navigation</title> 902<meta http-equiv="Content-Script-Type" content="text/javascript"> 903<meta http-equiv="Content-Type" content="text/html"> 904 905<style type="text/css"> 906 div.button td { padding-left: 5px; padding-right: 5px; 907 background-color: #eee; text-align: center; 908 border: 1px #444 outset; cursor: pointer; } 909 div.button a { font-weight: bold; color: black } 910 div.button td:hover { background: #ffcc99; } 911</style> 912EOF 913 914 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>" 915 916 cat << \EOF 917</head> 918<body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();" 919 onkeypress="keypress(event);"> 920 <noscript lang="javascript"> 921 <center> 922 <p><big>Framed Navigation controls require Javascript</big><br></br> 923 Either this browser is incompatable or javascript is not enabled</p> 924 </center> 925 </noscript> 926 <table width="100%" border="0" align="center"> 927 <tr> 928 <td valign="middle" width="25%">Diff navigation: 929 Use 'j' and 'k' for next and previous diffs; or use buttons 930 at right</td> 931 <td align="center" valign="top" width="50%"> 932 <div class="button"> 933 <table border="0" align="center"> 934 <tr> 935 <td> 936 <a onMouseDown="handlePress(1);return true;" 937 onMouseUp="handleRelease(1);return true;" 938 onMouseOut="handleRelease(1);return true;" 939 onClick="return false;" 940 title="Go to Beginning Of file">BOF</a></td> 941 <td> 942 <a onMouseDown="handlePress(3);return true;" 943 onMouseUp="handleRelease(3);return true;" 944 onMouseOut="handleRelease(3);return true;" 945 title="Scroll Up: Press and Hold to accelerate" 946 onClick="return false;">Scroll Up</a></td> 947 <td> 948 <a onMouseDown="handlePress(2);return true;" 949 onMouseUp="handleRelease(2);return true;" 950 onMouseOut="handleRelease(2);return true;" 951 title="Go to previous Diff" 952 onClick="return false;">Prev Diff</a> 953 </td></tr> 954 955 <tr> 956 <td> 957 <a onMouseDown="handlePress(6);return true;" 958 onMouseUp="handleRelease(6);return true;" 959 onMouseOut="handleRelease(6);return true;" 960 onClick="return false;" 961 title="Go to End Of File">EOF</a></td> 962 <td> 963 <a onMouseDown="handlePress(4);return true;" 964 onMouseUp="handleRelease(4);return true;" 965 onMouseOut="handleRelease(4);return true;" 966 title="Scroll Down: Press and Hold to accelerate" 967 onClick="return false;">Scroll Down</a></td> 968 <td> 969 <a onMouseDown="handlePress(5);return true;" 970 onMouseUp="handleRelease(5);return true;" 971 onMouseOut="handleRelease(5);return true;" 972 title="Go to next Diff" 973 onClick="return false;">Next Diff</a></td> 974 </tr> 975 </table> 976 </div> 977 </td> 978 <th valign="middle" width="25%"> 979 <form action="" name="diff" onsubmit="return ValidateDiffNum();"> 980 <input name="display" value="BOF" size="8" type="text"></input> 981 <input name="real" value="0" size="8" type="hidden"></input> 982 </form> 983 </th> 984 </tr> 985 </table> 986 </body> 987</html> 988EOF 989} 990 991 992 993# 994# diff_to_html <filename> <filepath> { U | C } <comment> 995# 996# Processes the output of diff to produce an HTML file representing either 997# context or unified diffs. 998# 999diff_to_html() 1000{ 1001 TNAME=$1 1002 TPATH=$2 1003 DIFFTYPE=$3 1004 COMMENT=$4 1005 1006 print "$HTML<head>$STDHEAD" 1007 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>" 1008 1009 if [[ $DIFFTYPE == "U" ]]; then 1010 print "$UDIFFCSS" 1011 fi 1012 1013 cat <<-EOF 1014 </head> 1015 <body id="SUNWwebrev"> 1016 <a class="print" href="javascript:print()">Print this page</a> 1017 <pre>$COMMENT</pre> 1018 <pre> 1019 EOF 1020 1021 html_quote | nawk ' 1022 /^--- new/ { next } 1023 /^\+\+\+ new/ { next } 1024 /^--- old/ { next } 1025 /^\*\*\* old/ { next } 1026 /^\*\*\*\*/ { next } 1027 /^-------/ { printf "<center><h1>%s</h1></center>\n", $0; next } 1028 /^\@\@.*\@\@$/ { printf "</pre><hr></hr><pre>\n"; 1029 printf "<span class=\"newmarker\">%s</span>\n", $0; 1030 next} 1031 1032 /^\*\*\*/ { printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0; 1033 next} 1034 /^---/ { printf "<span class=\"newmarker\">%s</span>\n", $0; 1035 next} 1036 /^\+/ {printf "<span class=\"new\">%s</span>\n", $0; next} 1037 /^!/ {printf "<span class=\"changed\">%s</span>\n", $0; next} 1038 /^-/ {printf "<span class=\"removed\">%s</span>\n", $0; next} 1039 {printf "%s\n", $0; next} 1040 ' 1041 1042 print "</pre></body></html>\n" 1043} 1044 1045 1046# 1047# source_to_html { new | old } <filename> 1048# 1049# Process a plain vanilla source file to transform it into an HTML file. 1050# 1051source_to_html() 1052{ 1053 WHICH=$1 1054 TNAME=$2 1055 1056 print "$HTML<head>$STDHEAD" 1057 print "<title>$WHICH $TNAME</title>" 1058 print "<body id=\"SUNWwebrev\">" 1059 print "<pre>" 1060 html_quote | nawk '{line += 1 ; printf "%4d %s\n", line, $0 }' 1061 print "</pre></body></html>" 1062} 1063 1064# 1065# teamwarecomments {text|html} parent-file child-file 1066# 1067# Find the first delta in the child that's not in the parent. Get the 1068# newest delta from the parent, get all deltas from the child starting 1069# with that delta, and then get all info starting with the second oldest 1070# delta in that list (the first delta unique to the child). 1071# 1072# This code adapted from Bill Shannon's "spc" script 1073# 1074comments_from_teamware() 1075{ 1076 fmt=$1 1077 pfile=$PWS/$2 1078 cfile=$CWS/$3 1079 1080 if [[ -f $pfile ]]; then 1081 psid=$(sccs prs -d:I: $pfile 2>/dev/null) 1082 else 1083 psid=1.1 1084 fi 1085 1086 set -A sids $(sccs prs -l -r$psid -d:I: $cfile 2>/dev/null) 1087 N=${#sids[@]} 1088 1089 nawkprg=' 1090 /^COMMENTS:/ {p=1; continue} 1091 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; } 1092 NF == 0u { continue } 1093 {if (p==0) continue; print $0 }' 1094 1095 if [[ $N -ge 2 ]]; then 1096 sid1=${sids[$((N-2))]} # Gets 2nd to last sid 1097 1098 if [[ $fmt == "text" ]]; then 1099 sccs prs -l -r$sid1 $cfile 2>/dev/null | \ 1100 nawk "$nawkprg" 1101 return 1102 fi 1103 1104 sccs prs -l -r$sid1 $cfile 2>/dev/null | \ 1105 html_quote | bug2url | sac2url | nawk "$nawkprg" 1106 fi 1107} 1108 1109# 1110# wxcomments {text|html} filepath 1111# 1112# Given the pathname of a file, find its location in a "wx" active file 1113# list and print the following sccs comment. Output is either text or 1114# HTML; if the latter, embedded bugids (sequence of 5 or more digits) are 1115# turned into URLs. 1116# 1117comments_from_wx() 1118{ 1119 typeset fmt=$1 1120 typeset p=$2 1121 1122 comm=`nawk ' 1123 $1 == "'$p'" { 1124 do getline ; while (NF > 0) 1125 getline 1126 while (NF > 0) { print ; getline } 1127 exit 1128 }' < $wxfile` 1129 1130 if [[ $fmt == "text" ]]; then 1131 print "$comm" 1132 return 1133 fi 1134 1135 print "$comm" | html_quote | bug2url | sac2url 1136} 1137 1138# 1139# getcomments {text|html} filepath parentpath 1140# 1141# Fetch the comments depending on what SCM mode we're in. 1142# 1143getcomments() 1144{ 1145 typeset fmt=$1 1146 typeset p=$2 1147 typeset pp=$3 1148 1149 if [[ -n $wxfile ]]; then 1150 comments_from_wx $fmt $p 1151 else 1152 if [[ $SCM_MODE == "teamware" ]]; then 1153 comments_from_teamware $fmt $pp $p 1154 fi 1155 fi 1156} 1157 1158# 1159# printCI <total-changed> <inserted> <deleted> <modified> <unchanged> 1160# 1161# Print out Code Inspection figures similar to sccs-prt(1) format. 1162# 1163function printCI 1164{ 1165 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5 1166 typeset str 1167 if (( tot == 1 )); then 1168 str="line" 1169 else 1170 str="lines" 1171 fi 1172 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \ 1173 $tot $str $ins $del $mod $unc 1174} 1175 1176 1177# 1178# difflines <oldfile> <newfile> 1179# 1180# Calculate and emit number of added, removed, modified and unchanged lines, 1181# and total lines changed, the sum of added + removed + modified. 1182# 1183function difflines 1184{ 1185 integer tot mod del ins unc err 1186 typeset filename 1187 1188 diff -e $1 $2 | eval $( nawk ' 1189 # Change range of lines: N,Nc 1190 /^[0-9]*,[0-9]*c$/ { 1191 n=split(substr($1,1,length($1)-1), counts, ","); 1192 if (n != 2) { 1193 error=2 1194 exit; 1195 } 1196 # 1197 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines. 1198 # following would be 5 - 3 = 2! Hence +1 for correction. 1199 # 1200 r=(counts[2]-counts[1])+1; 1201 1202 # 1203 # Now count replacement lines: each represents a change instead 1204 # of a delete, so increment c and decrement r. 1205 # 1206 while (getline != /^\.$/) { 1207 c++; 1208 r--; 1209 } 1210 # 1211 # If there were more replacement lines than original lines, 1212 # then r will be negative; in this case there are no deletions, 1213 # but there are r changes that should be counted as adds, and 1214 # since r is negative, subtract it from a and add it to c. 1215 # 1216 if (r < 0) { 1217 a-=r; 1218 c+=r; 1219 } 1220 1221 # 1222 # If there were more original lines than replacement lines, then 1223 # r will be positive; in this case, increment d by that much. 1224 # 1225 if (r > 0) { 1226 d+=r; 1227 } 1228 next; 1229 } 1230 1231 # Change lines: Nc 1232 /^[0-9].*c$/ { 1233 # The first line is a replacement; any more are additions. 1234 if (getline != /^\.$/) { 1235 c++; 1236 while (getline != /^\.$/) a++; 1237 } 1238 next; 1239 } 1240 1241 # Add lines: both Na and N,Na 1242 /^[0-9].*a$/ { 1243 while (getline != /^\.$/) a++; 1244 next; 1245 } 1246 1247 # Delete range of lines: N,Nd 1248 /^[0-9]*,[0-9]*d$/ { 1249 n=split(substr($1,1,length($1)-1), counts, ","); 1250 if (n != 2) { 1251 error=2 1252 exit; 1253 } 1254 # 1255 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines. 1256 # following would be 5 - 3 = 2! Hence +1 for correction. 1257 # 1258 r=(counts[2]-counts[1])+1; 1259 d+=r; 1260 next; 1261 } 1262 1263 # Delete line: Nd. For example 10d says line 10 is deleted. 1264 /^[0-9]*d$/ {d++; next} 1265 1266 # Should not get here! 1267 { 1268 error=1; 1269 exit; 1270 } 1271 1272 # Finish off - print results 1273 END { 1274 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n", 1275 (c+d+a), c, d, a, error); 1276 }' ) 1277 1278 # End of nawk, Check to see if any trouble occurred. 1279 if (( $? > 0 || err > 0 )); then 1280 print "Unexpected Error occurred reading" \ 1281 "\`diff -e $1 $2\`: \$?=$?, err=" $err 1282 return 1283 fi 1284 1285 # Accumulate totals 1286 (( TOTL += tot )) 1287 (( TMOD += mod )) 1288 (( TDEL += del )) 1289 (( TINS += ins )) 1290 # Calculate unchanged lines 1291 wc -l $1 | read unc filename 1292 if (( unc > 0 )); then 1293 (( unc -= del + mod )) 1294 (( TUNC += unc )) 1295 fi 1296 # print summary 1297 print "<span class=\"lineschanged\">" 1298 printCI $tot $ins $del $mod $unc 1299 print "</span>" 1300} 1301 1302 1303# 1304# flist_from_wx 1305# 1306# Sets up webrev to source its information from a wx-formatted file. 1307# Sets the global 'wxfile' variable. 1308# 1309function flist_from_wx 1310{ 1311 typeset argfile=$1 1312 if [[ -n ${argfile%%/*} ]]; then 1313 # 1314 # If the wx file pathname is relative then make it absolute 1315 # because the webrev does a "cd" later on. 1316 # 1317 wxfile=$PWD/$argfile 1318 else 1319 wxfile=$argfile 1320 fi 1321 1322 nawk '{ c = 1; print; 1323 while (getline) { 1324 if (NF == 0) { c = -c; continue } 1325 if (c > 0) print 1326 } 1327 }' $wxfile > $FLIST 1328 1329 print " Done." 1330} 1331 1332# 1333# flist_from_teamware [ <args-to-putback-n> ] 1334# 1335# Generate the file list by extracting file names from a putback -n. Some 1336# names may come from the "update/create" messages and others from the 1337# "currently checked out" warning. Renames are detected here too. Extract 1338# values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback 1339# -n as well, but remove them if they are already defined. 1340# 1341function flist_from_teamware 1342{ 1343 if [[ -n $codemgr_parent ]]; then 1344 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then 1345 print -u2 "parent $codemgr_parent doesn't look like a" \ 1346 "valid teamware workspace" 1347 exit 1 1348 fi 1349 parent_args="-p $codemgr_parent" 1350 fi 1351 1352 print " File list from: 'putback -n $parent_args $*' ... \c" 1353 1354 putback -n $parent_args $* 2>&1 | 1355 nawk ' 1356 /^update:|^create:/ {print $2} 1357 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)} 1358 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)} 1359 /^The following files are currently checked out/ {p = 1; continue} 1360 NF == 0 {p=0 ; continue} 1361 /^rename/ {old=$3} 1362 $1 == "to:" {print $2, old} 1363 /^"/ {continue} 1364 p == 1 {print $1}' | 1365 sort -r -k 1,1 -u | sort > $FLIST 1366 1367 print " Done." 1368} 1369 1370function env_from_flist 1371{ 1372 [[ -r $FLIST ]] || return 1373 1374 # 1375 # Use "eval" to set env variables that are listed in the file 1376 # list. Then copy those into our local versions of those 1377 # variables if they have not been set already. 1378 # 1379 eval `sed -e "s/#.*$//" $FLIST | grep = ` 1380 1381 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1382 1383 # 1384 # Check to see if CODEMGR_PARENT is set in the flist file. 1385 # 1386 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 1387 codemgr_parent=$CODEMGR_PARENT 1388} 1389 1390# 1391# detect_scm 1392# 1393# We dynamically test the SCM type; this allows future extensions to 1394# new SCM types 1395# 1396function detect_scm 1397{ 1398 # 1399 # If CODEMGR_WS is specified in the flist file, we assume teamware. 1400 # 1401 if [[ -r $FLIST ]]; then 1402 egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1 1403 if [[ $? -eq 0 ]]; then 1404 print "teamware" 1405 return 1406 fi 1407 fi 1408 1409 # 1410 # The presence of $CODEMGR_WS and a Codemgr_wsdata directory 1411 # is our clue that this is a teamware workspace. 1412 # 1413 if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then 1414 print "teamware" 1415 else 1416 print "unknown" 1417 fi 1418} 1419 1420function look_for_prog 1421{ 1422 typeset path 1423 typeset ppath 1424 typeset progname=$1 1425 1426 ppath=$PATH 1427 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin 1428 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin 1429 ppath=$ppath:/opt/onbld/bin/`/usr/bin/uname -p` 1430 1431 PATH=$ppath prog=`whence $progname` 1432 if [[ -n $prog ]]; then 1433 print $prog 1434 fi 1435} 1436 1437# 1438# Usage message. 1439# 1440function usage 1441{ 1442 print 'Usage:\twebrev [common-options] 1443 webrev [common-options] ( <file> | - ) 1444 webrev [common-options] -w <wx file> 1445 webrev [common-options] -l [arguments to 'putback'] 1446 1447Options: 1448 -O: Print bugids/arc cases suitable for OpenSolaris. 1449 -i <filename>: Include <filename> in the index.html file. 1450 -o <outdir>: Output webrev to specified directory. 1451 -p <compare-against>: Use specified parent wkspc or basis for comparison 1452 -w <wxfile>: Use specified wx active file. 1453 1454Environment: 1455 WDIR: Control the output directory. 1456 WEBREV_BUGURL: Control the URL prefix for bugids. 1457 WEBREV_SACURL: Control the URL prefix for ARC cases. 1458 1459SCM Environment: 1460 Teamware: CODEMGR_WS: Workspace location. 1461 Teamware: CODEMGR_PARENT: Parent workspace location. 1462' 1463 1464 exit 2 1465} 1466 1467# 1468# 1469# Main program starts here 1470# 1471# 1472 1473trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15 1474 1475set +o noclobber 1476 1477[[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` 1478[[ -z $WX ]] && WX=`look_for_prog wx` 1479[[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` 1480[[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf` 1481[[ -z $PERL ]] && PERL=`look_for_prog perl` 1482 1483if [[ ! -x $PERL ]]; then 1484 print -u2 "Error: No perl interpreter found. Exiting." 1485 exit 1 1486fi 1487 1488# 1489# These aren't fatal, but we want to note them to the user. 1490# We don't warn on the absence of 'wx' until later when we've 1491# determined that we actually need to try to invoke it. 1492# 1493[[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found." 1494[[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found." 1495[[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found." 1496 1497# Declare global total counters. 1498integer TOTL TINS TDEL TMOD TUNC 1499 1500flist_mode= 1501flist_file= 1502iflag= 1503oflag= 1504pflag= 1505lflag= 1506wflag= 1507Oflag= 1508while getopts "i:o:p:lwO" opt 1509do 1510 case $opt in 1511 i) iflag=1 1512 INCLUDE_FILE=$OPTARG;; 1513 1514 o) oflag=1 1515 WDIR=$OPTARG;; 1516 1517 p) pflag=1 1518 codemgr_parent=$OPTARG;; 1519 1520 # 1521 # If -l has been specified, we need to abort further options 1522 # processing, because subsequent arguments are going to be 1523 # arguments to 'putback -n'. 1524 # 1525 l) lflag=1 1526 break;; 1527 1528 w) wflag=1;; 1529 1530 O) Oflag=1;; 1531 1532 ?) usage;; 1533 esac 1534done 1535 1536FLIST=/tmp/$$.flist 1537 1538if [[ -n $wflag && -n $lflag ]]; then 1539 usage 1540fi 1541 1542# 1543# If this manually set as the parent, and it appears to be an earlier webrev, 1544# then note that fact and set the parent to the raw_files/new subdirectory. 1545# 1546if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then 1547 parent_webrev="$codemgr_parent" 1548 codemgr_parent="$codemgr_parent/raw_files/new" 1549fi 1550 1551if [[ -z $wflag && -z $lflag ]]; then 1552 shift $(($OPTIND - 1)) 1553 1554 if [[ $1 == "-" ]]; then 1555 cat > $FLIST 1556 flist_mode="stdin" 1557 flist_done=1 1558 shift 1559 elif [[ -n $1 ]]; then 1560 if [[ ! -r $1 ]]; then 1561 print -u2 "$1: no such file or not readable" 1562 usage 1563 fi 1564 cat $1 > $FLIST 1565 flist_mode="file" 1566 flist_file=$1 1567 flist_done=1 1568 shift 1569 else 1570 flist_mode="auto" 1571 fi 1572fi 1573 1574# 1575# Before we go on to further consider -l and -w, work out which SCM we think 1576# is in use. 1577# 1578SCM_MODE=`detect_scm $FLIST` 1579if [[ $SCM_MODE == "unknown" ]]; then 1580 print -u2 "Unable to determine SCM type currently in use." 1581 print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in" 1582 print -u2 " the environment or in the file list." 1583 exit 1 1584fi 1585 1586print -u2 " SCM detected: $SCM_MODE" 1587 1588if [[ -n $lflag ]]; then 1589 # 1590 # If the -l flag is given instead of the name of a file list, 1591 # then generate the file list by extracting file names from a 1592 # putback -n. 1593 # 1594 shift $(($OPTIND - 1)) 1595 flist_from_teamware "$*" 1596 flist_done=1 1597 shift $# 1598 1599elif [[ -n $wflag ]]; then 1600 # 1601 # If the -w is given then assume the file list is in Bonwick's "wx" 1602 # command format, i.e. pathname lines alternating with SCCS comment 1603 # lines with blank lines as separators. Use the SCCS comments later 1604 # in building the index.html file. 1605 # 1606 shift $(($OPTIND - 1)) 1607 wxfile=$1 1608 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then 1609 if [[ -r $CODEMGR_WS/wx/active ]]; then 1610 wxfile=$CODEMGR_WS/wx/active 1611 fi 1612 fi 1613 1614 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \ 1615 "be auto-detected (check \$CODEMGR_WS)" && exit 1 1616 1617 print -u2 " File list from: wx 'active' file '$wxfile' ... \c" 1618 flist_from_wx $wxfile 1619 flist_done=1 1620 if [[ -n "$*" ]]; then 1621 shift 1622 fi 1623elif [[ $flist_mode == "stdin" ]]; then 1624 print -u2 " File list from: standard input" 1625elif [[ $flist_mode == "file" ]]; then 1626 print -u2 " File list from: $flist_file" 1627fi 1628 1629if [[ $# -gt 0 ]]; then 1630 print -u2 "WARNING: unused arguments: $*" 1631fi 1632 1633if [[ $SCM_MODE == "teamware" ]]; then 1634 # 1635 # Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can 1636 # be set in a number of ways, in decreasing precedence: 1637 # 1638 # 1) on the command line (only for the parent) 1639 # 2) in the user environment 1640 # 3) in the flist 1641 # 4) automatically based on the workspace (only for the parent) 1642 # 1643 1644 # 1645 # Here is case (2): the user environment 1646 # 1647 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS 1648 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then 1649 print -u2 "$codemgr_ws: no such workspace" 1650 exit 1 1651 fi 1652 1653 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ 1654 codemgr_parent=$CODEMGR_PARENT 1655 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then 1656 print -u2 "$codemgr_parent: no such directory" 1657 exit 1 1658 fi 1659 1660 # 1661 # If we're in auto-detect mode and we haven't already gotten the file 1662 # list, then see if we can get it by probing for wx. 1663 # 1664 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then 1665 if [[ ! -x $WX ]]; then 1666 print -u2 "WARNING: wx not found!" 1667 fi 1668 1669 # 1670 # We need to use wx list -w so that we get renamed files, etc. 1671 # but only if a wx active file exists-- otherwise wx will 1672 # hang asking us to initialize our wx information. 1673 # 1674 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then 1675 print -u2 " File list from: 'wx list -w' ... \c" 1676 $WX list -w > $FLIST 1677 $WX comments > /tmp/$$.wx_comments 1678 wxfile=/tmp/$$.wx_comments 1679 print -u2 "done" 1680 flist_done=1 1681 fi 1682 fi 1683 1684 # 1685 # If by hook or by crook we've gotten a file list by now (perhaps 1686 # from the command line), eval it to extract environment variables from 1687 # it: This is step (3). 1688 # 1689 env_from_flist 1690 1691 # 1692 # Continuing step (3): If we still have no file list, we'll try to get 1693 # it from teamware. 1694 # 1695 if [[ -z $flist_done ]]; then 1696 flist_from_teamware 1697 env_from_flist 1698 fi 1699 1700 # 1701 # Observe true directory name of CODEMGR_WS, as used later in 1702 # webrev title. 1703 # 1704 codemgr_ws=$(cd $codemgr_ws;print $PWD) 1705 1706 # 1707 # (4) If we still don't have a value for codemgr_parent, get it 1708 # from workspace. 1709 # 1710 [[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent` 1711 if [[ ! -d $codemgr_parent ]]; then 1712 print -u2 "$CODEMGR_PARENT: no such parent workspace" 1713 exit 1 1714 fi 1715 1716 # 1717 # Reset CODEMGR_WS to make sure teamware commands are happy. 1718 # 1719 CODEMGR_WS=$codemgr_ws 1720 CWS=$codemgr_ws 1721 PWS=$codemgr_parent 1722fi 1723 1724# 1725# If the user didn't specify a -i option, check to see if there is a 1726# webrev-info file in the workspace directory. 1727# 1728if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then 1729 iflag=1 1730 INCLUDE_FILE="$CWS/webrev-info" 1731fi 1732 1733if [[ -n $iflag ]]; then 1734 if [[ ! -r $INCLUDE_FILE ]]; then 1735 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \ 1736 "not readable." 1737 exit 1 1738 else 1739 # 1740 # $INCLUDE_FILE may be a relative path, and the script alters 1741 # PWD, so we just stash a copy in /tmp. 1742 # 1743 cp $INCLUDE_FILE /tmp/$$.include 1744 fi 1745fi 1746 1747# 1748# Output directory. 1749# 1750WDIR=${WDIR:-$CWS/webrev} 1751 1752# 1753# Name of the webrev, derived from the workspace name; in the 1754# future this could potentially be an option. 1755# 1756WNAME=${CWS##*/} 1757 1758if [ "${WDIR%%/*}" ]; then 1759 WDIR=$PWD/$WDIR 1760fi 1761 1762if [[ ! -d $WDIR ]]; then 1763 mkdir -p $WDIR 1764 [[ $? != 0 ]] && exit 1 1765fi 1766 1767# 1768# Summarize what we're going to do. 1769# 1770print " Workspace: $CWS" 1771if [[ -n $parent_webrev ]]; then 1772 print "Compare against: webrev at $parent_webrev" 1773else 1774 print "Compare against: $PWS" 1775fi 1776 1777[[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE" 1778print " Output to: $WDIR" 1779 1780# 1781# Save the file list in the webrev dir 1782# 1783[[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list 1784 1785# 1786# Bug IDs will be replaced by a URL. Order of precedence 1787# is: default location, $WEBREV_BUGURL, the -O flag. 1788# 1789BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr=' 1790[[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL" 1791[[ -n "$Oflag" ]] && \ 1792 BUGURL='http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=' 1793 1794# 1795# Likewise, ARC cases will be replaced by a URL. Order of precedence 1796# is: default, $WEBREV_SACURL, the -O flag. 1797# 1798# Note that -O also triggers different substitution behavior for 1799# SACURL. See sac2url(). 1800# 1801SACURL='http://sac.eng.sun.com' 1802[[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL" 1803[[ -n "$Oflag" ]] && \ 1804 SACURL='http://www.opensolaris.org/os/community/arc/caselog' 1805 1806rm -f $WDIR/$WNAME.patch 1807rm -f $WDIR/$WNAME.ps 1808rm -f $WDIR/$WNAME.pdf 1809 1810touch $WDIR/$WNAME.patch 1811 1812print " Output Files:" 1813 1814# 1815# Clean up the file list: Remove comments, blank lines and env variables. 1816# 1817sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean 1818FLIST=/tmp/$$.flist.clean 1819 1820# 1821# First pass through the files: generate the per-file webrev HTML-files. 1822# 1823cat $FLIST | while read LINE 1824do 1825 set - $LINE 1826 P=$1 1827 1828 # 1829 # Normally, each line in the file list is just a pathname of a 1830 # file that has been modified or created in the child. A file 1831 # that is renamed in the child workspace has two names on the 1832 # line: new name followed by the old name. 1833 # 1834 oldname="" 1835 oldpath="" 1836 rename= 1837 if [[ $# -eq 2 ]]; then 1838 PP=$2 # old filename 1839 oldname=" (was $PP)" 1840 oldpath="$PP" 1841 rename=1 1842 PDIR=${PP%/*} 1843 if [[ $PDIR == $PP ]]; then 1844 PDIR="." # File at root of workspace 1845 fi 1846 1847 PF=${PP##*/} 1848 1849 DIR=${P%/*} 1850 if [[ $DIR == $P ]]; then 1851 DIR="." # File at root of workspace 1852 fi 1853 1854 F=${P##*/} 1855 1856 else 1857 DIR=${P%/*} 1858 if [[ "$DIR" == "$P" ]]; then 1859 DIR="." # File at root of workspace 1860 fi 1861 1862 F=${P##*/} 1863 1864 PP=$P 1865 PDIR=$DIR 1866 PF=$F 1867 fi 1868 1869 COMM=`getcomments html $P $PP` 1870 1871 if [[ ! -d $CWS/$DIR ]]; then 1872 print " $CWS/$DIR: no such directory" 1873 continue 1874 fi 1875 1876 print "\t$P$oldname\n\t\t\c" 1877 1878 # Make the webrev mirror directory if necessary 1879 mkdir -p $WDIR/$DIR 1880 1881 # cd to the directory so the names are short 1882 cd $CWS/$DIR 1883 1884 # 1885 # If we're in OpenSolaris mode, we enforce a minor policy: 1886 # help to make sure the reviewer doesn't accidentally publish 1887 # source which is in usr/closed/* or deleted_files/usr/closed/* 1888 # 1889 if [[ -n "$Oflag" ]]; then 1890 pclosed=${P##usr/closed/} 1891 pdeleted=${P##deleted_files/usr/closed/} 1892 if [[ "$pclosed" != "$P" || "$pdeleted" != "$P" ]]; then 1893 print "*** Omitting closed source for OpenSolaris" \ 1894 "mode review" 1895 continue 1896 fi 1897 fi 1898 1899 # 1900 # We stash old and new files into parallel directories in /tmp 1901 # and do our diffs there. This makes it possible to generate 1902 # clean looking diffs which don't have absolute paths present. 1903 # 1904 olddir=$WDIR/raw_files/old 1905 newdir=$WDIR/raw_files/new 1906 mkdir -p $olddir 1907 mkdir -p $newdir 1908 mkdir -p $olddir/$PDIR 1909 mkdir -p $newdir/$DIR 1910 1911 if [[ $SCM_MODE == "teamware" ]]; then 1912 # If the child's version doesn't exist then 1913 # get a readonly copy. 1914 1915 if [[ ! -f $F && -f SCCS/s.$F ]]; then 1916 sccs get -s $F 1917 fi 1918 1919 # 1920 # Snag new version of file. 1921 # 1922 rm -f $newdir/$DIR/$F 1923 cp $F $newdir/$DIR/$F 1924 1925 # 1926 # Get the parent's version of the file. First see whether the 1927 # child's version is checked out and get the parent's version 1928 # with keywords expanded or unexpanded as appropriate. 1929 # 1930 if [ -f "$PWS/$PDIR/SCCS/s.$PF" -o \ 1931 -f "$PWS/$PDIR/SCCS/p.$PF" ]; then 1932 rm -f $olddir/$PDIR/$PF 1933 if [ -f "SCCS/p.$F" ]; then 1934 sccs get -s -p -k $PWS/$PDIR/$PF \ 1935 > $olddir/$PDIR/$PF 1936 else 1937 sccs get -s -p $PWS/$PDIR/$PF \ 1938 > $olddir/$PDIR/$PF 1939 fi 1940 else 1941 if [[ -f $PWS/$PDIR/$PF ]]; then 1942 # Parent is not a real workspace, but just a raw 1943 # directory tree - use the file that's there as 1944 # the old file. 1945 1946 rm -f $olddir/$DIR/$F 1947 cp $PWS/$PDIR/$PF $olddir/$DIR/$F 1948 fi 1949 fi 1950 fi 1951 1952 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then 1953 print "*** Error: file not in parent or child" 1954 continue 1955 fi 1956 1957 cd $WDIR/raw_files 1958 ofile=old/$PDIR/$PF 1959 nfile=new/$DIR/$F 1960 1961 mv_but_nodiff= 1962 cmp $ofile $nfile > /dev/null 2>&1 1963 if [[ $? == 0 && $rename == 1 ]]; then 1964 mv_but_nodiff=1 1965 fi 1966 1967 # 1968 # If we have old and new versions of the file then run the appropriate 1969 # diffs. This is complicated by a couple of factors: 1970 # 1971 # - renames must be handled specially: we emit a 'remove' 1972 # diff and an 'add' diff 1973 # - new files and deleted files must be handled specially 1974 # - Solaris patch(1m) can't cope with file creation 1975 # (and hence renames) as of this writing. 1976 # - To make matters worse, gnu patch doesn't interpret the 1977 # output of Solaris diff properly when it comes to 1978 # adds and deletes. We need to do some "cleansing" 1979 # transformations: 1980 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ 1981 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ 1982 # 1983 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" 1984 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" 1985 1986 rm -f $WDIR/$DIR/$F.patch 1987 if [[ -z $rename ]]; then 1988 if [ ! -f "$ofile" ]; then 1989 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 1990 > $WDIR/$DIR/$F.patch 1991 elif [ ! -f "$nfile" ]; then 1992 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 1993 > $WDIR/$DIR/$F.patch 1994 else 1995 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch 1996 fi 1997 else 1998 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 1999 > $WDIR/$DIR/$F.patch 2000 2001 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2002 >> $WDIR/$DIR/$F.patch 2003 2004 fi 2005 2006 # 2007 # Tack the patch we just made onto the accumulated patch for the 2008 # whole wad. 2009 # 2010 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch 2011 2012 print " patch\c" 2013 2014 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2015 2016 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff 2017 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2018 > $WDIR/$DIR/$F.cdiff.html 2019 print " cdiffs\c" 2020 2021 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff 2022 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2023 > $WDIR/$DIR/$F.udiff.html 2024 2025 print " udiffs\c" 2026 2027 if [[ -x $WDIFF ]]; then 2028 $WDIFF -c "$COMM" \ 2029 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ 2030 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2031 if [[ $? -eq 0 ]]; then 2032 print " wdiffs\c" 2033 else 2034 print " wdiffs[fail]\c" 2035 fi 2036 fi 2037 2038 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2039 > $WDIR/$DIR/$F.sdiff.html 2040 print " sdiffs\c" 2041 2042 print " frames\c" 2043 2044 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2045 2046 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2047 2048 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2049 # renamed file: may also have differences 2050 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2051 elif [[ -f $nfile ]]; then 2052 # new file: count added lines 2053 difflines /dev/null $nfile > $WDIR/$DIR/$F.count 2054 elif [[ -f $ofile ]]; then 2055 # old file: count deleted lines 2056 difflines $ofile /dev/null > $WDIR/$DIR/$F.count 2057 fi 2058 2059 # 2060 # Now we generate the postscript for this file. We generate diffs 2061 # only in the event that there is delta, or the file is new (it seems 2062 # tree-killing to print out the contents of deleted files). 2063 # 2064 if [[ -f $nfile ]]; then 2065 ocr=$ofile 2066 [[ ! -f $ofile ]] && ocr=/dev/null 2067 2068 if [[ -z $mv_but_nodiff ]]; then 2069 textcomm=`getcomments text $P $PP` 2070 if [[ -x $CODEREVIEW ]]; then 2071 $CODEREVIEW -y "$textcomm" \ 2072 -e $ocr $nfile \ 2073 > /tmp/$$.psfile 2>/dev/null && 2074 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps 2075 if [[ $? -eq 0 ]]; then 2076 print " ps\c" 2077 else 2078 print " ps[fail]\c" 2079 fi 2080 fi 2081 fi 2082 fi 2083 2084 if [[ -f $ofile && -z $mv_but_nodiff ]]; then 2085 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html 2086 print " old\c" 2087 fi 2088 2089 if [[ -f $nfile ]]; then 2090 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html 2091 print " new\c" 2092 fi 2093 2094 print 2095done 2096 2097frame_nav_js > $WDIR/ancnav.js 2098frame_navigation > $WDIR/ancnav.html 2099 2100if [[ ! -f $WDIR/$WNAME.ps ]]; then 2101 print " Generating PDF: Skipped: no output available" 2102elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then 2103 print " Generating PDF: \c" 2104 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf 2105 rm -f $WDIR/$WNAME.ps 2106 print "Done." 2107else 2108 print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'" 2109fi 2110 2111# If we're in OpenSolaris mode and there's a closed dir under $WDIR, 2112# delete it - prevent accidental publishing of closed source 2113 2114if [[ -n "$Oflag" ]]; then 2115 /usr/bin/find $WDIR -type d -name closed -exec /bin/rm -rf {} \; 2116fi 2117 2118# Now build the index.html file that contains 2119# links to the source files and their diffs. 2120 2121cd $CWS 2122 2123# Save total changed lines for Code Inspection. 2124print "$TOTL" > $WDIR/TotalChangedLines 2125 2126print " index.html: \c" 2127INDEXFILE=$WDIR/index.html 2128exec 3<&1 # duplicate stdout to FD3. 2129exec 1<&- # Close stdout. 2130exec > $INDEXFILE # Open stdout to index file. 2131 2132print "$HTML<head>$STDHEAD" 2133print "<title>$WNAME</title>" 2134print "</head>" 2135print "<body id=\"SUNWwebrev\">" 2136print "<div class=\"summary\">" 2137print "<h2>Code Review for $WNAME</h2>" 2138 2139print "<table>" 2140 2141# 2142# Figure out the username and gcos name. To maintain compatibility 2143# with passwd(4), we must support '&' substitutions. 2144# 2145username=`id | cut -d '(' -f 2 | cut -d ')' -f 1` 2146realname=`getent passwd $username | cut -d':' -f 5` 2147userupper=`$PERL -e "print ucfirst $username"` 2148realname=`print $realname | sed s/\&/$userupper/` 2149date="on `date`" 2150 2151if [[ -n "$username" && -n "$realname" ]]; then 2152 print "<tr><th>Prepared by:</th>" 2153 print "<td>$realname ($username) $date</td></tr>" 2154elif [[ -n "$username" ]]; then 2155 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>" 2156fi 2157 2158print "<tr><th>Workspace:</th><td>$CWS</td></tr>" 2159print "<tr><th>Compare against:</th><td>" 2160if [[ -n $parent_webrev ]]; then 2161 print "webrev at $parent_webrev" 2162else 2163 print "$PWS" 2164fi 2165print "</td></tr>" 2166print "<tr><th>Summary of changes:</th><td>" 2167printCI $TOTL $TINS $TDEL $TMOD $TUNC 2168print "</td></tr>" 2169 2170if [[ -f $WDIR/$WNAME.patch ]]; then 2171 print "<tr><th>Patch of changes:</th><td>" 2172 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>" 2173fi 2174if [[ -f $WDIR/$WNAME.pdf ]]; then 2175 print "<tr><th>Printable review:</th><td>" 2176 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>" 2177fi 2178 2179if [[ -n "$iflag" ]]; then 2180 print "<tr><th>Author comments:</th><td><div>" 2181 cat /tmp/$$.include 2182 print "</div></td></tr>" 2183fi 2184print "</table>" 2185print "</div>" 2186 2187 2188# 2189# Second pass through the files: generate the rest of the index file 2190# 2191cat $FLIST | while read LINE 2192do 2193 set - $LINE 2194 P=$1 2195 2196 if [[ $# == 2 ]]; then 2197 PP=$2 2198 oldname=" <i>(was $PP)</i>" 2199 2200 else 2201 PP=$P 2202 oldname="" 2203 fi 2204 2205 DIR=${P%/*} 2206 if [[ $DIR == $P ]]; then 2207 DIR="." # File at root of workspace 2208 fi 2209 2210 # Avoid processing the same file twice. 2211 # It's possible for renamed files to 2212 # appear twice in the file list 2213 2214 F=$WDIR/$P 2215 2216 print "<p>" 2217 2218 # If there's a diffs file, make diffs links 2219 2220 if [[ -f $F.cdiff.html ]]; then 2221 print "<a href=\"$P.cdiff.html\">Cdiffs</a>" 2222 print "<a href=\"$P.udiff.html\">Udiffs</a>" 2223 2224 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 2225 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 2226 fi 2227 2228 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 2229 2230 print "<a href=\"$P.frames.html\">Frames</a>" 2231 else 2232 print " ------ ------ ------" 2233 2234 if [[ -x $WDIFF ]]; then 2235 print " ------" 2236 fi 2237 2238 print " ------" 2239 fi 2240 2241 # If there's an old file, make the link 2242 2243 if [[ -f $F-.html ]]; then 2244 print "<a href=\"$P-.html\">Old</a>" 2245 else 2246 print " ---" 2247 fi 2248 2249 # If there's an new file, make the link 2250 2251 if [[ -f $F.html ]]; then 2252 print "<a href=\"$P.html\">New</a>" 2253 else 2254 print " ---" 2255 fi 2256 2257 if [[ -f $F.patch ]]; then 2258 print "<a href=\"$P.patch\">Patch</a>" 2259 else 2260 print " -----" 2261 fi 2262 2263 if [[ -f $WDIR/raw_files/new/$P ]]; then 2264 print "<a href=\"raw_files/new/$P\">Raw</a>" 2265 else 2266 print " ---" 2267 fi 2268 2269 print "<b>$P</b> $oldname" 2270 2271 # 2272 # Check for usr/closed and deleted_files/usr/closed 2273 # 2274 if [ ! -z "$Oflag" ]; then 2275 if [[ $P == usr/closed/* || \ 2276 $P == deleted_files/usr/closed/* ]]; then 2277 print " <i>Closed source: omitted from" \ 2278 "this review</i>" 2279 fi 2280 fi 2281 2282 print "</p>" 2283 # Insert delta comments 2284 2285 print "<blockquote><pre>" 2286 getcomments html $P $PP 2287 print "</pre>" 2288 2289 # Add additional comments comment 2290 2291 print "<!-- Add comments to explain changes in $P here -->" 2292 2293 # Add count of changes. 2294 2295 if [[ -f $F.count ]]; then 2296 cat $F.count 2297 rm $F.count 2298 fi 2299 print "</blockquote>" 2300done 2301 2302print 2303print 2304print "<hr></hr>" 2305print "<p style=\"font-size: small\">" 2306print "This code review page was prepared using <b>$0</b>" 2307print "(vers $WEBREV_UPDATED)." 2308print "Webrev is maintained by the <a href=\"http://www.opensolaris.org\">" 2309print "OpenSolaris</a> project. The latest version may be obtained" 2310print "<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>" 2311print "</body>" 2312print "</html>" 2313 2314exec 1<&- # Close FD 1. 2315exec 1<&3 # dup FD 3 to restore stdout. 2316exec 3<&- # close FD 3. 2317 2318print "Done." 2319