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