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