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