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