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/* 1890 # 1891 if [[ -n $Oflag ]]; then 1892 pclosed=${P##usr/closed/} 1893 if [[ $pclosed != $P ]]; then 1894 print "*** Omitting closed source for OpenSolaris" \ 1895 "mode review" 1896 continue 1897 fi 1898 fi 1899 1900 # 1901 # We stash old and new files into parallel directories in /tmp 1902 # and do our diffs there. This makes it possible to generate 1903 # clean looking diffs which don't have absolute paths present. 1904 # 1905 olddir=$WDIR/raw_files/old 1906 newdir=$WDIR/raw_files/new 1907 mkdir -p $olddir 1908 mkdir -p $newdir 1909 mkdir -p $olddir/$PDIR 1910 mkdir -p $newdir/$DIR 1911 1912 if [[ $SCM_MODE == "teamware" ]]; then 1913 # If the child's version doesn't exist then 1914 # get a readonly copy. 1915 1916 if [[ ! -f $F && -f SCCS/s.$F ]]; then 1917 sccs get -s $F 1918 fi 1919 1920 # 1921 # Snag new version of file. 1922 # 1923 rm -f $newdir/$DIR/$F 1924 cp $F $newdir/$DIR/$F 1925 1926 # 1927 # Get the parent's version of the file. First see whether the 1928 # child's version is checked out and get the parent's version 1929 # with keywords expanded or unexpanded as appropriate. 1930 # 1931 if [ -f $PWS/$PDIR/SCCS/s.$PF -o \ 1932 -f $PWS/$PDIR/SCCS/p.$PF ]; then 1933 rm -f $olddir/$PDIR/$PF 1934 if [ -f SCCS/p.$F ]; then 1935 sccs get -s -p -k $PWS/$PDIR/$PF \ 1936 > $olddir/$PDIR/$PF 1937 else 1938 sccs get -s -p $PWS/$PDIR/$PF \ 1939 > $olddir/$PDIR/$PF 1940 fi 1941 else 1942 if [[ -f $PWS/$PDIR/$PF ]]; then 1943 # Parent is not a real workspace, but just a raw 1944 # directory tree - use the file that's there as 1945 # the old file. 1946 1947 rm -f $olddir/$DIR/$F 1948 cp $PWS/$PDIR/$PF $olddir/$DIR/$F 1949 fi 1950 fi 1951 fi 1952 1953 if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then 1954 print "*** Error: file not in parent or child" 1955 continue 1956 fi 1957 1958 cd $WDIR/raw_files 1959 ofile=old/$PDIR/$PF 1960 nfile=new/$DIR/$F 1961 1962 mv_but_nodiff= 1963 cmp $ofile $nfile > /dev/null 2>&1 1964 if [[ $? == 0 && $rename == 1 ]]; then 1965 mv_but_nodiff=1 1966 fi 1967 1968 # 1969 # If we have old and new versions of the file then run the appropriate 1970 # diffs. This is complicated by a couple of factors: 1971 # 1972 # - renames must be handled specially: we emit a 'remove' 1973 # diff and an 'add' diff 1974 # - new files and deleted files must be handled specially 1975 # - Solaris patch(1m) can't cope with file creation 1976 # (and hence renames) as of this writing. 1977 # - To make matters worse, gnu patch doesn't interpret the 1978 # output of Solaris diff properly when it comes to 1979 # adds and deletes. We need to do some "cleansing" 1980 # transformations: 1981 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ 1982 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ 1983 # 1984 cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" 1985 cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" 1986 1987 rm -f $WDIR/$DIR/$F.patch 1988 if [[ -z $rename ]]; then 1989 if [ ! -f $ofile ]; then 1990 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 1991 > $WDIR/$DIR/$F.patch 1992 elif [ ! -f $nfile ]; then 1993 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 1994 > $WDIR/$DIR/$F.patch 1995 else 1996 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch 1997 fi 1998 else 1999 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ 2000 > $WDIR/$DIR/$F.patch 2001 2002 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ 2003 >> $WDIR/$DIR/$F.patch 2004 2005 fi 2006 2007 # 2008 # Tack the patch we just made onto the accumulated patch for the 2009 # whole wad. 2010 # 2011 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch 2012 2013 print " patch\c" 2014 2015 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then 2016 2017 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff 2018 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ 2019 > $WDIR/$DIR/$F.cdiff.html 2020 print " cdiffs\c" 2021 2022 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff 2023 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ 2024 > $WDIR/$DIR/$F.udiff.html 2025 2026 print " udiffs\c" 2027 2028 if [[ -x $WDIFF ]]; then 2029 $WDIFF -c "$COMM" \ 2030 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ 2031 $WDIR/$DIR/$F.wdiff.html 2>/dev/null 2032 if [[ $? -eq 0 ]]; then 2033 print " wdiffs\c" 2034 else 2035 print " wdiffs[fail]\c" 2036 fi 2037 fi 2038 2039 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ 2040 > $WDIR/$DIR/$F.sdiff.html 2041 print " sdiffs\c" 2042 2043 print " frames\c" 2044 2045 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff 2046 2047 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2048 2049 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then 2050 # renamed file: may also have differences 2051 difflines $ofile $nfile > $WDIR/$DIR/$F.count 2052 elif [[ -f $nfile ]]; then 2053 # new file: count added lines 2054 difflines /dev/null $nfile > $WDIR/$DIR/$F.count 2055 elif [[ -f $ofile ]]; then 2056 # old file: count deleted lines 2057 difflines $ofile /dev/null > $WDIR/$DIR/$F.count 2058 fi 2059 2060 # 2061 # Now we generate the postscript for this file. We generate diffs 2062 # only in the event that there is delta, or the file is new (it seems 2063 # tree-killing to print out the contents of deleted files). 2064 # 2065 if [[ -f $nfile ]]; then 2066 ocr=$ofile 2067 [[ ! -f $ofile ]] && ocr=/dev/null 2068 2069 if [[ -z $mv_but_nodiff ]]; then 2070 textcomm=`getcomments text $P $PP` 2071 if [[ -x $CODEREVIEW ]]; then 2072 $CODEREVIEW -y "$textcomm" \ 2073 -e $ocr $nfile \ 2074 > /tmp/$$.psfile 2>/dev/null && 2075 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps 2076 if [[ $? -eq 0 ]]; then 2077 print " ps\c" 2078 else 2079 print " ps[fail]\c" 2080 fi 2081 fi 2082 fi 2083 fi 2084 2085 if [[ -f $ofile && -z $mv_but_nodiff ]]; then 2086 source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html 2087 print " old\c" 2088 fi 2089 2090 if [[ -f $nfile ]]; then 2091 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html 2092 print " new\c" 2093 fi 2094 2095 print 2096done 2097 2098frame_nav_js > $WDIR/ancnav.js 2099frame_navigation > $WDIR/ancnav.html 2100 2101if [[ ! -f $WDIR/$WNAME.ps ]]; then 2102 print " Generating PDF: Skipped: no output available" 2103elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then 2104 print " Generating PDF: \c" 2105 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf 2106 rm -f $WDIR/$WNAME.ps 2107 print "Done." 2108else 2109 print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'" 2110fi 2111 2112# Now build the index.html file that contains 2113# links to the source files and their diffs. 2114 2115cd $CWS 2116 2117# Save total changed lines for Code Inspection. 2118print "$TOTL" > $WDIR/TotalChangedLines 2119 2120print " index.html: \c" 2121INDEXFILE=$WDIR/index.html 2122exec 3<&1 # duplicate stdout to FD3. 2123exec 1<&- # Close stdout. 2124exec > $INDEXFILE # Open stdout to index file. 2125 2126print "$HTML<head>$STDHEAD" 2127print "<title>$WNAME</title>" 2128print "</head>" 2129print "<body id=\"SUNWwebrev\">" 2130print "<div class=\"summary\">" 2131print "<h2>Code Review for $WNAME</h2>" 2132 2133print "<table>" 2134 2135# 2136# Figure out the username and gcos name. To maintain compatibility 2137# with passwd(4), we must support '&' substitutions. 2138# 2139username=`id | cut -d '(' -f 2 | cut -d ')' -f 1` 2140realname=`getent passwd $username | cut -d':' -f 5` 2141userupper=`$PERL -e "print ucfirst $username"` 2142realname=`print $realname | sed s/\&/$userupper/` 2143date="on `date`" 2144 2145if [[ -n "$username" && -n "$realname" ]]; then 2146 print "<tr><th>Prepared by:</th>" 2147 print "<td>$realname ($username) $date</td></tr>" 2148elif [[ -n "$username" ]]; then 2149 print "<tr><th>Prepared by:</th><td>$username $date</td></tr>" 2150fi 2151 2152print "<tr><th>Workspace:</th><td>$CWS</td></tr>" 2153print "<tr><th>Compare against:</th><td>" 2154if [[ -n $parent_webrev ]]; then 2155 print "webrev at $parent_webrev" 2156else 2157 print "$PWS" 2158fi 2159print "</td></tr>" 2160print "<tr><th>Summary of changes:</th><td>" 2161printCI $TOTL $TINS $TDEL $TMOD $TUNC 2162print "</td></tr>" 2163 2164if [[ -f $WDIR/$WNAME.patch ]]; then 2165 print "<tr><th>Patch of changes:</th><td>" 2166 print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>" 2167fi 2168if [[ -f $WDIR/$WNAME.pdf ]]; then 2169 print "<tr><th>Printable review:</th><td>" 2170 print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>" 2171fi 2172 2173if [[ -n "$iflag" ]]; then 2174 print "<tr><th>Author comments:</th><td><div>" 2175 cat /tmp/$$.include 2176 print "</div></td></tr>" 2177fi 2178print "</table>" 2179print "</div>" 2180 2181 2182# 2183# Second pass through the files: generate the rest of the index file 2184# 2185cat $FLIST | while read LINE 2186do 2187 set - $LINE 2188 P=$1 2189 2190 if [[ $# == 2 ]]; then 2191 PP=$2 2192 oldname=" <i>(was $PP)</i>" 2193 2194 else 2195 PP=$P 2196 oldname="" 2197 fi 2198 2199 DIR=${P%/*} 2200 if [[ $DIR == $P ]]; then 2201 DIR="." # File at root of workspace 2202 fi 2203 2204 # Avoid processing the same file twice. 2205 # It's possible for renamed files to 2206 # appear twice in the file list 2207 2208 F=$WDIR/$P 2209 2210 print "<p>" 2211 2212 # If there's a diffs file, make diffs links 2213 2214 if [[ -f $F.cdiff.html ]]; then 2215 print "<a href=\"$P.cdiff.html\">Cdiffs</a>" 2216 print "<a href=\"$P.udiff.html\">Udiffs</a>" 2217 2218 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then 2219 print "<a href=\"$P.wdiff.html\">Wdiffs</a>" 2220 fi 2221 2222 print "<a href=\"$P.sdiff.html\">Sdiffs</a>" 2223 2224 print "<a href=\"$P.frames.html\">Frames</a>" 2225 else 2226 print " ------ ------ ------" 2227 2228 if [[ -x $WDIFF ]]; then 2229 print " ------" 2230 fi 2231 2232 print " ------" 2233 fi 2234 2235 # If there's an old file, make the link 2236 2237 if [[ -f $F-.html ]]; then 2238 print "<a href=\"$P-.html\">Old</a>" 2239 else 2240 print " ---" 2241 fi 2242 2243 # If there's an new file, make the link 2244 2245 if [[ -f $F.html ]]; then 2246 print "<a href=\"$P.html\">New</a>" 2247 else 2248 print " ---" 2249 fi 2250 2251 if [[ -f $F.patch ]]; then 2252 print "<a href=\"$P.patch\">Patch</a>" 2253 else 2254 print " -----" 2255 fi 2256 2257 if [[ -f $WDIR/raw_files/new/$P ]]; then 2258 print "<a href=\"raw_files/new/$P\">Raw</a>" 2259 else 2260 print " ---" 2261 fi 2262 2263 print "<b>$P</b> $oldname" 2264 2265 # 2266 # Check for usr/closed 2267 # 2268 if [ ! -z "$Oflag" ]; then 2269 if [[ $P == usr/closed/* ]]; then 2270 print " <i>Closed source: omitted from" \ 2271 "this review</i>" 2272 fi 2273 fi 2274 2275 print "</p>" 2276 # Insert delta comments 2277 2278 print "<blockquote><pre>" 2279 getcomments html $P $PP 2280 print "</pre>" 2281 2282 # Add additional comments comment 2283 2284 print "<!-- Add comments to explain changes in $P here -->" 2285 2286 # Add count of changes. 2287 2288 if [[ -f $F.count ]]; then 2289 cat $F.count 2290 rm $F.count 2291 fi 2292 print "</blockquote>" 2293done 2294 2295print 2296print 2297print "<hr />" 2298print "<p style=\"font-size: small\">" 2299print "This code review page was prepared using <b>$0</b>" 2300print "(vers $WEBREV_UPDATED)." 2301print "Webrev is maintained by the <a href=\"http://www.opensolaris.org\">" 2302print "OpenSolaris</a> project. The latest version may be obtained" 2303print "<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>" 2304print "</body>" 2305print "</html>" 2306 2307exec 1<&- # Close FD 1. 2308exec 1<&3 # dup FD 3 to restore stdout. 2309exec 3<&- # close FD 3. 2310 2311print "Done." 2312