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