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