xref: /titanic_52/usr/src/tools/scripts/webrev.sh (revision b9bd317cda1afb3a01f4812de73e8cec888cbbd7)
1#!/usr/bin/ksh -p
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22#
23# ident	"%Z%%M%	%I%	%E% SMI"
24#
25# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
26# Use is subject to license terms.
27#
28# This script takes a file list and a workspace and builds a set of html files
29# suitable for doing a code review of source changes via a web page.
30# Documentation is available via the manual page, webrev.1, or just
31# type 'webrev -h'.
32#
33# Acknowledgements to contributors to webrev are listed in the webrev(1)
34# man page.
35#
36
37#
38# The following variable is set to SCCS delta date 20YY/MM/DD.
39# Note this will have to be changed in 2100 or when SCCS has support for
40# 4 digit years; whichever is the sooner!
41#
42WEBREV_UPDATED=20%E%
43
44REMOVED_COLOR=brown
45CHANGED_COLOR=blue
46NEW_COLOR=blue
47
48HTML='<?xml version="1.0"?>
49<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
50    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
51<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
52
53FRAMEHTML='<?xml version="1.0"?>
54<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
55    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
56<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
57
58STDHEAD='<meta http-equiv="cache-control" content="no-cache"></meta>
59<meta http-equiv="Pragma" content="no-cache"></meta>
60<meta http-equiv="Expires" content="-1"></meta>
61<!--
62   Note to customizers: the body of the webrev is IDed as SUNWwebrev
63   to allow easy overriding by users of webrev via the userContent.css
64   mechanism available in some browsers.
65
66   For example, to have all "removed" information be red instead of
67   brown, set a rule in your userContent.css file like:
68
69       body#SUNWwebrev span.removed { color: red ! important; }
70-->
71<style type="text/css" media="screen">
72body {
73    background-color: #eeeeee;
74}
75hr {
76    border: none 0;
77    border-top: 1px solid #aaa;
78    height: 1px;
79}
80div.summary {
81    font-size: .8em;
82    border-bottom: 1px solid #aaa;
83    padding-left: 1em;
84    padding-right: 1em;
85}
86div.summary h2 {
87    margin-bottom: 0.3em;
88}
89div.summary table th {
90    text-align: right;
91    vertical-align: top;
92    white-space: nowrap;
93}
94span.lineschanged {
95    font-size: 0.7em;
96}
97span.oldmarker {
98    color: red;
99    font-size: large;
100    font-weight: bold;
101}
102span.newmarker {
103    color: green;
104    font-size: large;
105    font-weight: bold;
106}
107span.removed {
108    color: brown;
109}
110span.changed {
111    color: blue;
112}
113span.new {
114    color: blue;
115    font-weight: bold;
116}
117a.print { font-size: x-small; }
118a:hover { background-color: #ffcc99; }
119</style>
120
121<style type="text/css" media="print">
122pre { font-size: 0.8em; font-family: courier, monospace; }
123span.removed { color: #444; font-style: italic }
124span.changed { font-weight: bold; }
125span.new { font-weight: bold; }
126span.newmarker { font-size: 1.2em; font-weight: bold; }
127span.oldmarker { font-size: 1.2em; font-weight: bold; }
128a.print {display: none}
129hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
130</style>
131'
132
133#
134# UDiffs need a slightly different CSS rule for 'new' items (we don't
135# want them to be bolded as we do in cdiffs or sdiffs).
136#
137UDIFFCSS='
138<style type="text/css" media="screen">
139span.new {
140    color: blue;
141    font-weight: normal;
142}
143</style>
144'
145
146#
147# input_cmd | html_quote | output_cmd
148# or
149# html_quote filename | output_cmd
150#
151# Make a piece of source code safe for display in an HTML <pre> block.
152#
153html_quote()
154{
155	sed -e "s/&/\&amp;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" "$@" | expand
156}
157
158#
159# input_cmd | bug2url | output_cmd
160#
161# Scan for bugids and insert <a> links to the relevent bug database.
162#
163bug2url()
164{
165	sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL'&\">&</a>|g'
166}
167
168#
169# input_cmd | sac2url | output_cmd
170#
171# Scan for ARC cases and insert <a> links to the relevent SAC database.
172# This is slightly complicated because inside the SWAN, SAC cases are
173# grouped by ARC: PSARC/2006/123.  But on OpenSolaris.org, they are
174# referenced as 2006/123 (without labelling the ARC).
175#
176sac2url()
177{
178	if [[ -z "$Oflag" ]]; then
179	    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'
180	else
181	    sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'/\2/\3\">\1 \2/\3</a>|g'
182	fi
183}
184
185#
186# strip_unchanged <infile> | output_cmd
187#
188# Removes chunks of sdiff documents that have not changed. This makes it
189# easier for a code reviewer to find the bits that have changed.
190#
191# Deleted lines of text are replaced by a horizontal rule. Some
192# identical lines are retained before and after the changed lines to
193# provide some context.  The number of these lines is controlled by the
194# variable C in the nawk script below.
195#
196# The script detects changed lines as any line that has a "<span class="
197# string embedded (unchanged lines have no particular class and are not
198# part of a <span>).  Blank lines (without a sequence number) are also
199# detected since they flag lines that have been inserted or deleted.
200#
201strip_unchanged()
202{
203	nawk '
204	BEGIN	{ C = c = 20 }
205	NF == 0 || /span class=/ {
206		if (c > C) {
207			c -= C
208			inx = 0
209			if (c > C) {
210				print "\n</pre><hr></hr><pre>"
211				inx = c % C
212				c = C
213			}
214
215			for (i = 0; i < c; i++)
216				print ln[(inx + i) % C]
217		}
218		c = 0;
219		print
220		next
221	}
222	{	if (c >= C) {
223			ln[c % C] = $0
224			c++;
225			next;
226		}
227		c++;
228		print
229	}
230	END	{ if (c > (C * 2)) print "\n</pre><hr></hr>" }
231
232	' $1
233}
234
235#
236# sdiff_to_html
237#
238# This function takes two files as arguments, obtains their diff, and
239# processes the diff output to present the files as an HTML document with
240# the files displayed side-by-side, differences shown in color.  It also
241# takes a delta comment, rendered as an HTML snippet, as the third
242# argument.  The function takes two files as arguments, then the name of
243# file, the path, and the comment.  The HTML will be delivered on stdout,
244# e.g.
245#
246#   $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
247#         new/usr/src/tools/scripts/webrev.sh \
248#         webrev.sh usr/src/tools/scripts \
249#         '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
250#          1234567</a> my bugid' > <file>.html
251#
252# framed_sdiff() is then called which creates $2.frames.html
253# in the webrev tree.
254#
255# FYI: This function is rather unusual in its use of awk.  The initial
256# diff run produces conventional diff output showing changed lines mixed
257# with editing codes.  The changed lines are ignored - we're interested in
258# the editing codes, e.g.
259#
260#      8c8
261#      57a61
262#      63c66,76
263#      68,93d80
264#      106d90
265#      108,110d91
266#
267#  These editing codes are parsed by the awk script and used to generate
268#  another awk script that generates HTML, e.g the above lines would turn
269#  into something like this:
270#
271#      BEGIN { printf "<pre>\n" }
272#      function sp(n) {for (i=0;i<n;i++)printf "\n"}
273#      function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
274#      NR==8           {wl("#7A7ADD");next}
275#      NR==54          {wl("#7A7ADD");sp(3);next}
276#      NR==56          {wl("#7A7ADD");next}
277#      NR==57          {wl("black");printf "\n"; next}
278#        :               :
279#
280#  This script is then run on the original source file to generate the
281#  HTML that corresponds to the source file.
282#
283#  The two HTML files are then combined into a single piece of HTML that
284#  uses an HTML table construct to present the files side by side.  You'll
285#  notice that the changes are color-coded:
286#
287#   black     - unchanged lines
288#   blue      - changed lines
289#   bold blue - new lines
290#   brown     - deleted lines
291#
292#  Blank lines are inserted in each file to keep unchanged lines in sync
293#  (side-by-side).  This format is familiar to users of sdiff(1) or
294#  Teamware's filemerge tool.
295#
296sdiff_to_html()
297{
298	diff -b $1 $2 > /tmp/$$.diffs
299
300	TNAME=$3
301	TPATH=$4
302	COMMENT=$5
303
304	#
305	#  Now we have the diffs, generate the HTML for the old file.
306	#
307	nawk '
308	BEGIN	{
309		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
310		printf "function removed() "
311		printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
312		printf "function changed() "
313		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
314		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
315}
316	/^</	{next}
317	/^>/	{next}
318	/^---/	{next}
319
320	{
321	split($1, a, /[cad]/) ;
322	if (index($1, "a")) {
323		if (a[1] == 0) {
324			n = split(a[2], r, /,/);
325			if (n == 1)
326				printf "BEGIN\t\t{sp(1)}\n"
327			else
328				printf "BEGIN\t\t{sp(%d)}\n",\
329				(r[2] - r[1]) + 1
330			next
331		}
332
333		printf "NR==%s\t\t{", a[1]
334		n = split(a[2], r, /,/);
335		s = r[1];
336		if (n == 1)
337			printf "bl();printf \"\\n\"; next}\n"
338		else {
339			n = r[2] - r[1]
340			printf "bl();sp(%d);next}\n",\
341			(r[2] - r[1]) + 1
342		}
343		next
344	}
345	if (index($1, "d")) {
346		n = split(a[1], r, /,/);
347		n1 = r[1]
348		n2 = r[2]
349		if (n == 1)
350			printf "NR==%s\t\t{removed(); next}\n" , n1
351		else
352			printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
353		next
354	}
355	if (index($1, "c")) {
356		n = split(a[1], r, /,/);
357		n1 = r[1]
358		n2 = r[2]
359		final = n2
360		d1 = 0
361		if (n == 1)
362			printf "NR==%s\t\t{changed();" , n1
363		else {
364			d1 = n2 - n1
365			printf "NR==%s,NR==%s\t{changed();" , n1, n2
366		}
367		m = split(a[2], r, /,/);
368		n1 = r[1]
369		n2 = r[2]
370		if (m > 1) {
371			d2  = n2 - n1
372			if (d2 > d1) {
373				if (n > 1) printf "if (NR==%d)", final
374				printf "sp(%d);", d2 - d1
375			}
376		}
377		printf "next}\n" ;
378
379		next
380	}
381	}
382
383	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
384	' /tmp/$$.diffs > /tmp/$$.file1
385
386	#
387	#  Now generate the HTML for the new file
388	#
389	nawk '
390	BEGIN	{
391		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
392		printf "function new() "
393		printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
394		printf "function changed() "
395		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
396		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
397	}
398
399	/^</	{next}
400	/^>/	{next}
401	/^---/	{next}
402
403	{
404	split($1, a, /[cad]/) ;
405	if (index($1, "d")) {
406		if (a[2] == 0) {
407			n = split(a[1], r, /,/);
408			if (n == 1)
409				printf "BEGIN\t\t{sp(1)}\n"
410			else
411				printf "BEGIN\t\t{sp(%d)}\n",\
412				(r[2] - r[1]) + 1
413			next
414		}
415
416		printf "NR==%s\t\t{", a[2]
417		n = split(a[1], r, /,/);
418		s = r[1];
419		if (n == 1)
420			printf "bl();printf \"\\n\"; next}\n"
421		else {
422			n = r[2] - r[1]
423			printf "bl();sp(%d);next}\n",\
424			(r[2] - r[1]) + 1
425		}
426		next
427	}
428	if (index($1, "a")) {
429		n = split(a[2], r, /,/);
430		n1 = r[1]
431		n2 = r[2]
432		if (n == 1)
433			printf "NR==%s\t\t{new() ; next}\n" , n1
434		else
435			printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
436		next
437	}
438	if (index($1, "c")) {
439		n = split(a[2], r, /,/);
440		n1 = r[1]
441		n2 = r[2]
442		final = n2
443		d2 = 0;
444		if (n == 1) {
445			final = n1
446			printf "NR==%s\t\t{changed();" , n1
447		} else {
448			d2 = n2 - n1
449			printf "NR==%s,NR==%s\t{changed();" , n1, n2
450		}
451		m = split(a[1], r, /,/);
452		n1 = r[1]
453		n2 = r[2]
454		if (m > 1) {
455			d1  = n2 - n1
456			if (d1 > d2) {
457				if (n > 1) printf "if (NR==%d)", final
458				printf "sp(%d);", d1 - d2
459			}
460		}
461		printf "next}\n" ;
462		next
463	}
464	}
465	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
466	' /tmp/$$.diffs > /tmp/$$.file2
467
468	#
469	# Post-process the HTML files by running them back through nawk
470	#
471	html_quote < $1 | nawk -f /tmp/$$.file1 > /tmp/$$.file1.html
472
473	html_quote < $2 | nawk -f /tmp/$$.file2 > /tmp/$$.file2.html
474
475	#
476	# Now combine into a valid HTML file and side-by-side into a table
477	#
478	print "$HTML<head>$STDHEAD"
479	print "<title>$WNAME Sdiff $TPATH </title>"
480	print "</head><body id=\"SUNWwebrev\">"
481        print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
482	print "<pre>$COMMENT</pre>\n"
483	print "<table><tr valign=\"top\">"
484	print "<td><pre>"
485
486	strip_unchanged /tmp/$$.file1.html
487
488	print "</pre></td><td><pre>"
489
490	strip_unchanged /tmp/$$.file2.html
491
492	print "</pre></td>"
493	print "</tr></table>"
494	print "</body></html>"
495
496	framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
497	    "$COMMENT"
498}
499
500
501#
502# framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
503#
504# Expects lefthand and righthand side html files created by sdiff_to_html.
505# We use insert_anchors() to augment those with HTML navigation anchors,
506# and then emit the main frame.  Content is placed into:
507#
508#    $WDIR/DIR/$TNAME.lhs.html
509#    $WDIR/DIR/$TNAME.rhs.html
510#    $WDIR/DIR/$TNAME.frames.html
511#
512# NOTE: We rely on standard usage of $WDIR and $DIR.
513#
514function framed_sdiff
515{
516	typeset TNAME=$1
517	typeset TPATH=$2
518	typeset lhsfile=$3
519	typeset rhsfile=$4
520	typeset comments=$5
521	typeset RTOP
522
523	# Enable html files to access WDIR via a relative path.
524	RTOP=$(relative_dir $TPATH $WDIR)
525
526	# Make the rhs/lhs files and output the frameset file.
527	print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
528
529	cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
530	    <script type="text/javascript" src="$RTOP/ancnav.js"></script>
531	    </head>
532	    <body id="SUNWwebrev" onkeypress="keypress(event);">
533	    <a name="0"></a>
534	    <pre>$comments</pre><hr></hr>
535	EOF
536
537	cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
538
539	insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
540	insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
541
542	close='</body></html>'
543
544	print $close >> $WDIR/$DIR/$TNAME.lhs.html
545	print $close >> $WDIR/$DIR/$TNAME.rhs.html
546
547	print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
548	print "<title>$WNAME Framed-Sdiff " \
549	    "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
550	cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
551	  <frameset rows="*,60">
552	    <frameset cols="50%,50%">
553	      <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs"></frame>
554	      <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame>
555	    </frameset>
556	  <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0"
557	   marginheight="0" name="nav"></frame>
558	  <noframes>
559            <body id="SUNWwebrev">
560	      Alas 'frames' webrev requires that your browser supports frames
561	      and has the feature enabled.
562            </body>
563	  </noframes>
564	  </frameset>
565	</html>
566	EOF
567}
568
569
570#
571# fix_postscript
572#
573# Merge codereview output files to a single conforming postscript file, by:
574# 	- removing all extraneous headers/trailers
575#	- making the page numbers right
576#	- removing pages devoid of contents which confuse some
577#	  postscript readers.
578#
579# From Casper.
580#
581function fix_postscript
582{
583	infile=$1
584
585	cat > /tmp/$$.crmerge.pl << \EOF
586
587	print scalar(<>);		# %!PS-Adobe---
588	print "%%Orientation: Landscape\n";
589
590	$pno = 0;
591	$doprint = 1;
592
593	$page = "";
594
595	while (<>) {
596		next if (/^%%Pages:\s*\d+/);
597
598		if (/^%%Page:/) {
599			if ($pno == 0 || $page =~ /\)S/) {
600				# Header or single page containing text
601				print "%%Page: ? $pno\n" if ($pno > 0);
602				print $page;
603				$pno++;
604			} else {
605				# Empty page, skip it.
606			}
607			$page = "";
608			$doprint = 1;
609			next;
610		}
611
612		# Skip from %%Trailer of one document to Endprolog
613		# %%Page of the next
614		$doprint = 0 if (/^%%Trailer/);
615		$page .= $_ if ($doprint);
616	}
617
618	if ($page =~ /\)S/) {
619		print "%%Page: ? $pno\n";
620		print $page;
621	} else {
622		$pno--;
623	}
624	print "%%Trailer\n%%Pages: $pno\n";
625EOF
626
627	$PERL /tmp/$$.crmerge.pl < $infile
628}
629
630
631#
632# input_cmd | insert_anchors | output_cmd
633#
634# Flag blocks of difference with sequentially numbered invisible
635# anchors.  These are used to drive the frames version of the
636# sdiffs output.
637#
638# NOTE: Anchor zero flags the top of the file irrespective of changes,
639# an additional anchor is also appended to flag the bottom.
640#
641# The script detects changed lines as any line that has a "<span
642# class=" string embedded (unchanged lines have no class set and are
643# not part of a <span>.  Blank lines (without a sequence number)
644# are also detected since they flag lines that have been inserted or
645# deleted.
646#
647function insert_anchors
648{
649	nawk '
650	function ia() {
651		printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
652	}
653
654	BEGIN {
655		anc=1;
656		inblock=1;
657		printf "<pre>\n";
658	}
659	NF == 0 || /^<span class=/ {
660		if (inblock == 0) {
661			ia();
662			inblock=1;
663		}
664		print;
665		next;
666	}
667	{
668		inblock=0;
669		print;
670	}
671	END {
672		ia();
673
674		printf "<b style=\"font-size: large; color: red\">";
675		printf "--- EOF ---</b>"
676        	for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
677		printf "</pre>"
678		printf "<form name=\"eof\">";
679		printf "<input name=\"value\" value=\"%d\" " \
680		    "type=\"hidden\"></input>", anc - 1;
681		printf "</form>";
682	}
683	' $1
684}
685
686
687#
688# relative_dir
689#
690# Print a relative return path from $1 to $2.  For example if
691# $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
692# this function would print "../../../../".
693#
694# In the event that $1 is not in $2 a warning is printed to stderr,
695# and $2 is returned-- the result of this is that the resulting webrev
696# is not relocatable.
697#
698function relative_dir
699{
700	typeset cur="${1##$2?(/)}"
701	typeset ret=""
702	if [[ $2 == $cur ]]; then   # Should never happen.
703		# Should never happen.
704		print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
705		print -u2 "to \"$2\".  Check input paths.  Framed webrev "
706		print -u2 "will not be relocatable!"
707		print $2
708		return
709	fi
710
711	while [[ -n ${cur} ]];
712	do
713		cur=${cur%%*(/)*([!/])}
714		if [[ -z $ret ]]; then
715			ret=".."
716		else
717			ret="../$ret"
718		fi
719	done
720	print $ret
721}
722
723
724#
725# frame_nav_js
726#
727# Emit javascript for frame navigation
728#
729function frame_nav_js
730{
731cat << \EOF
732var myInt;
733var scrolling=0;
734var sfactor = 3;
735var scount=10;
736
737function scrollByPix() {
738	if (scount<=0) {
739		sfactor*=1.2;
740		scount=10;
741	}
742	parent.lhs.scrollBy(0,sfactor);
743	parent.rhs.scrollBy(0,sfactor);
744	scount--;
745}
746
747function scrollToAnc(num) {
748
749	// Update the value of the anchor in the form which we use as
750	// storage for this value.  setAncValue() will take care of
751	// correcting for overflow and underflow of the value and return
752	// us the new value.
753	num = setAncValue(num);
754
755	// Set location and scroll back a little to expose previous
756	// lines.
757	//
758	// Note that this could be improved: it is possible although
759	// complex to compute the x and y position of an anchor, and to
760	// scroll to that location directly.
761	//
762	parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
763	parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
764
765	parent.lhs.scrollBy(0,-30);
766	parent.rhs.scrollBy(0,-30);
767}
768
769function getAncValue()
770{
771	return (parseInt(parent.nav.document.diff.real.value));
772}
773
774function setAncValue(val)
775{
776	if (val <= 0) {
777		val = 0;
778		parent.nav.document.diff.real.value = val;
779		parent.nav.document.diff.display.value = "BOF";
780		return (val);
781	}
782
783	//
784	// The way we compute the max anchor value is to stash it
785	// inline in the left and right hand side pages-- it's the same
786	// on each side, so we pluck from the left.
787	//
788	maxval = parent.lhs.document.eof.value.value;
789	if (val < maxval) {
790		parent.nav.document.diff.real.value = val;
791		parent.nav.document.diff.display.value = val.toString();
792		return (val);
793	}
794
795	// this must be: val >= maxval
796	val = maxval;
797	parent.nav.document.diff.real.value = val;
798	parent.nav.document.diff.display.value = "EOF";
799	return (val);
800}
801
802function stopScroll() {
803	if (scrolling==1) {
804		clearInterval(myInt);
805		scrolling=0;
806	}
807}
808
809function startScroll() {
810	stopScroll();
811	scrolling=1;
812	myInt=setInterval("scrollByPix()",10);
813}
814
815function handlePress(b) {
816
817	switch (b) {
818	    case 1 :
819		scrollToAnc(-1);
820		break;
821	    case 2 :
822		scrollToAnc(getAncValue() - 1);
823		break;
824	    case 3 :
825		sfactor=-3;
826		startScroll();
827		break;
828	    case 4 :
829		sfactor=3;
830		startScroll();
831		break;
832	    case 5 :
833		scrollToAnc(getAncValue() + 1);
834		break;
835	    case 6 :
836		scrollToAnc(999999);
837		break;
838	}
839}
840
841function handleRelease(b) {
842	stopScroll();
843}
844
845function keypress(ev) {
846	var keynum;
847	var keychar;
848
849	if (window.event) { // IE
850		keynum = ev.keyCode;
851	} else if (ev.which) { // non-IE
852		keynum = ev.which;
853	}
854
855	keychar = String.fromCharCode(keynum);
856
857	if (keychar == "k") {
858		handlePress(2);
859		return (0);
860	} else if (keychar == "j" || keychar == " ") {
861		handlePress(5);
862		return (0);
863	}
864	return (1);
865}
866
867function ValidateDiffNum(){
868	val = parent.nav.document.diff.display.value;
869	if (val == "EOF") {
870		scrollToAnc(999999);
871		return;
872	}
873
874	if (val == "BOF") {
875		scrollToAnc(0);
876		return;
877	}
878
879        i=parseInt(val);
880        if (isNaN(i)) {
881                parent.nav.document.diff.display.value = getAncValue();
882        } else {
883                scrollToAnc(i);
884        }
885        return false;
886}
887
888EOF
889}
890
891#
892# frame_navigation
893#
894# Output anchor navigation file for framed sdiffs.
895#
896function frame_navigation
897{
898	print "$HTML<head>$STDHEAD"
899
900	cat << \EOF
901<title>Anchor Navigation</title>
902<meta http-equiv="Content-Script-Type" content="text/javascript">
903<meta http-equiv="Content-Type" content="text/html">
904
905<style type="text/css">
906    div.button td { padding-left: 5px; padding-right: 5px;
907		    background-color: #eee; text-align: center;
908		    border: 1px #444 outset; cursor: pointer; }
909    div.button a { font-weight: bold; color: black }
910    div.button td:hover { background: #ffcc99; }
911</style>
912EOF
913
914	print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
915
916	cat << \EOF
917</head>
918<body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
919	onkeypress="keypress(event);">
920    <noscript lang="javascript">
921      <center>
922	<p><big>Framed Navigation controls require Javascript</big><br></br>
923	Either this browser is incompatable or javascript is not enabled</p>
924      </center>
925    </noscript>
926    <table width="100%" border="0" align="center">
927	<tr>
928          <td valign="middle" width="25%">Diff navigation:
929          Use 'j' and 'k' for next and previous diffs; or use buttons
930          at right</td>
931	  <td align="center" valign="top" width="50%">
932	    <div class="button">
933	      <table border="0" align="center">
934                  <tr>
935		    <td>
936		      <a onMouseDown="handlePress(1);return true;"
937			 onMouseUp="handleRelease(1);return true;"
938			 onMouseOut="handleRelease(1);return true;"
939			 onClick="return false;"
940			 title="Go to Beginning Of file">BOF</a></td>
941		    <td>
942		      <a onMouseDown="handlePress(3);return true;"
943			 onMouseUp="handleRelease(3);return true;"
944			 onMouseOut="handleRelease(3);return true;"
945			 title="Scroll Up: Press and Hold to accelerate"
946			 onClick="return false;">Scroll Up</a></td>
947		    <td>
948		      <a onMouseDown="handlePress(2);return true;"
949			 onMouseUp="handleRelease(2);return true;"
950			 onMouseOut="handleRelease(2);return true;"
951			 title="Go to previous Diff"
952			 onClick="return false;">Prev Diff</a>
953		    </td></tr>
954
955		  <tr>
956		    <td>
957		      <a onMouseDown="handlePress(6);return true;"
958			 onMouseUp="handleRelease(6);return true;"
959			 onMouseOut="handleRelease(6);return true;"
960			 onClick="return false;"
961			 title="Go to End Of File">EOF</a></td>
962		    <td>
963		      <a onMouseDown="handlePress(4);return true;"
964			 onMouseUp="handleRelease(4);return true;"
965			 onMouseOut="handleRelease(4);return true;"
966			 title="Scroll Down: Press and Hold to accelerate"
967			 onClick="return false;">Scroll Down</a></td>
968		    <td>
969		      <a onMouseDown="handlePress(5);return true;"
970			 onMouseUp="handleRelease(5);return true;"
971			 onMouseOut="handleRelease(5);return true;"
972			 title="Go to next Diff"
973			 onClick="return false;">Next Diff</a></td>
974		  </tr>
975              </table>
976	    </div>
977	  </td>
978	  <th valign="middle" width="25%">
979	    <form action="" name="diff" onsubmit="return ValidateDiffNum();">
980		<input name="display" value="BOF" size="8" type="text"></input>
981		<input name="real" value="0" size="8" type="hidden"></input>
982	    </form>
983	  </th>
984	</tr>
985    </table>
986  </body>
987</html>
988EOF
989}
990
991
992
993#
994# diff_to_html <filename> <filepath> { U | C } <comment>
995#
996# Processes the output of diff to produce an HTML file representing either
997# context or unified diffs.
998#
999diff_to_html()
1000{
1001	TNAME=$1
1002	TPATH=$2
1003	DIFFTYPE=$3
1004	COMMENT=$4
1005
1006	print "$HTML<head>$STDHEAD"
1007	print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
1008
1009	if [[ $DIFFTYPE == "U" ]]; then
1010		print "$UDIFFCSS"
1011	fi
1012
1013	cat <<-EOF
1014	</head>
1015	<body id="SUNWwebrev">
1016        <a class="print" href="javascript:print()">Print this page</a>
1017	<pre>$COMMENT</pre>
1018        <pre>
1019	EOF
1020
1021	html_quote | nawk '
1022	/^--- new/	{ next }
1023	/^\+\+\+ new/	{ next }
1024	/^--- old/	{ next }
1025	/^\*\*\* old/	{ next }
1026	/^\*\*\*\*/	{ next }
1027	/^-------/	{ printf "<center><h1>%s</h1></center>\n", $0; next }
1028	/^\@\@.*\@\@$/	{ printf "</pre><hr></hr><pre>\n";
1029			  printf "<span class=\"newmarker\">%s</span>\n", $0;
1030			  next}
1031
1032	/^\*\*\*/	{ printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0;
1033			  next}
1034	/^---/		{ printf "<span class=\"newmarker\">%s</span>\n", $0;
1035			  next}
1036	/^\+/		{printf "<span class=\"new\">%s</span>\n", $0; next}
1037	/^!/		{printf "<span class=\"changed\">%s</span>\n", $0; next}
1038	/^-/		{printf "<span class=\"removed\">%s</span>\n", $0; next}
1039			{printf "%s\n", $0; next}
1040	'
1041
1042	print "</pre></body></html>\n"
1043}
1044
1045
1046#
1047# source_to_html { new | old } <filename>
1048#
1049# Process a plain vanilla source file to transform it into an HTML file.
1050#
1051source_to_html()
1052{
1053	WHICH=$1
1054	TNAME=$2
1055
1056	print "$HTML<head>$STDHEAD"
1057	print "<title>$WHICH $TNAME</title>"
1058	print "<body id=\"SUNWwebrev\">"
1059	print "<pre>"
1060	html_quote | nawk '{line += 1 ; printf "%4d %s\n", line, $0 }'
1061	print "</pre></body></html>"
1062}
1063
1064#
1065# teamwarecomments {text|html} parent-file child-file
1066#
1067# Find the first delta in the child that's not in the parent.  Get the
1068# newest delta from the parent, get all deltas from the child starting
1069# with that delta, and then get all info starting with the second oldest
1070# delta in that list (the first delta unique to the child).
1071#
1072# This code adapted from Bill Shannon's "spc" script
1073#
1074comments_from_teamware()
1075{
1076	fmt=$1
1077	pfile=$PWS/$2
1078	cfile=$CWS/$3
1079
1080	if [[ -f $pfile ]]; then
1081		psid=$(sccs prs -d:I: $pfile 2>/dev/null)
1082	else
1083		psid=1.1
1084	fi
1085
1086	set -A sids $(sccs prs -l -r$psid -d:I: $cfile 2>/dev/null)
1087	N=${#sids[@]}
1088
1089	nawkprg='
1090		/^COMMENTS:/	{p=1; continue}
1091		/^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
1092		NF == 0u	{ continue }
1093		{if (p==0) continue; print $0 }'
1094
1095	if [[ $N -ge 2 ]]; then
1096		sid1=${sids[$((N-2))]}	# Gets 2nd to last sid
1097
1098		if [[ $fmt == "text" ]]; then
1099			sccs prs -l -r$sid1 $cfile  2>/dev/null | \
1100			    nawk "$nawkprg"
1101			return
1102		fi
1103
1104		sccs prs -l -r$sid1 $cfile  2>/dev/null | \
1105		    html_quote | bug2url | sac2url | nawk "$nawkprg"
1106	fi
1107}
1108
1109#
1110# wxcomments {text|html} filepath
1111#
1112# Given the pathname of a file, find its location in a "wx" active file
1113# list and print the following sccs comment.  Output is either text or
1114# HTML; if the latter, embedded bugids (sequence of 5 or more digits) are
1115# turned into URLs.
1116#
1117comments_from_wx()
1118{
1119	typeset fmt=$1
1120	typeset p=$2
1121
1122	comm=`nawk '
1123	$1 == "'$p'" {
1124		do getline ; while (NF > 0)
1125		getline
1126		while (NF > 0) { print ; getline }
1127		exit
1128	}' < $wxfile`
1129
1130	if [[ $fmt == "text" ]]; then
1131		print "$comm"
1132		return
1133	fi
1134
1135	print "$comm" | html_quote | bug2url | sac2url
1136}
1137
1138#
1139# getcomments {text|html} filepath parentpath
1140#
1141# Fetch the comments depending on what SCM mode we're in.
1142#
1143getcomments()
1144{
1145	typeset fmt=$1
1146	typeset p=$2
1147	typeset pp=$3
1148
1149	if [[ -n $wxfile ]]; then
1150		comments_from_wx $fmt $p
1151	else
1152		if [[ $SCM_MODE == "teamware" ]]; then
1153			comments_from_teamware $fmt $pp $p
1154		fi
1155	fi
1156}
1157
1158#
1159# printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1160#
1161# Print out Code Inspection figures similar to sccs-prt(1) format.
1162#
1163function printCI
1164{
1165	integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1166	typeset str
1167	if (( tot == 1 )); then
1168		str="line"
1169	else
1170		str="lines"
1171	fi
1172	printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \
1173	    $tot $str $ins $del $mod $unc
1174}
1175
1176
1177#
1178# difflines <oldfile> <newfile>
1179#
1180# Calculate and emit number of added, removed, modified and unchanged lines,
1181# and total lines changed, the sum of added + removed + modified.
1182#
1183function difflines
1184{
1185	integer tot mod del ins unc err
1186	typeset filename
1187
1188	diff -e $1 $2 | eval $( nawk '
1189	# Change range of lines: N,Nc
1190	/^[0-9]*,[0-9]*c$/ {
1191		n=split(substr($1,1,length($1)-1), counts, ",");
1192		if (n != 2) {
1193		    error=2
1194		    exit;
1195		}
1196		#
1197		# 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
1198		# following would be 5 - 3 = 2! Hence +1 for correction.
1199		#
1200		r=(counts[2]-counts[1])+1;
1201
1202		#
1203		# Now count replacement lines: each represents a change instead
1204		# of a delete, so increment c and decrement r.
1205		#
1206		while (getline != /^\.$/) {
1207			c++;
1208			r--;
1209		}
1210		#
1211		# If there were more replacement lines than original lines,
1212		# then r will be negative; in this case there are no deletions,
1213		# but there are r changes that should be counted as adds, and
1214		# since r is negative, subtract it from a and add it to c.
1215		#
1216		if (r < 0) {
1217			a-=r;
1218			c+=r;
1219		}
1220
1221		#
1222		# If there were more original lines than replacement lines, then
1223		# r will be positive; in this case, increment d by that much.
1224		#
1225		if (r > 0) {
1226			d+=r;
1227		}
1228		next;
1229	}
1230
1231	# Change lines: Nc
1232	/^[0-9].*c$/ {
1233		# The first line is a replacement; any more are additions.
1234		if (getline != /^\.$/) {
1235			c++;
1236			while (getline != /^\.$/) a++;
1237		}
1238		next;
1239	}
1240
1241	# Add lines: both Na and N,Na
1242	/^[0-9].*a$/ {
1243		while (getline != /^\.$/) a++;
1244		next;
1245	}
1246
1247	# Delete range of lines: N,Nd
1248	/^[0-9]*,[0-9]*d$/ {
1249		n=split(substr($1,1,length($1)-1), counts, ",");
1250		if (n != 2) {
1251			error=2
1252			exit;
1253		}
1254		#
1255		# 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
1256		# following would be 5 - 3 = 2! Hence +1 for correction.
1257		#
1258		r=(counts[2]-counts[1])+1;
1259		d+=r;
1260		next;
1261	}
1262
1263	# Delete line: Nd.   For example 10d says line 10 is deleted.
1264	/^[0-9]*d$/ {d++; next}
1265
1266	# Should not get here!
1267	{
1268		error=1;
1269		exit;
1270	}
1271
1272	# Finish off - print results
1273	END {
1274		printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
1275		    (c+d+a), c, d, a, error);
1276	}' )
1277
1278	# End of nawk, Check to see if any trouble occurred.
1279	if (( $? > 0 || err > 0 )); then
1280		print "Unexpected Error occurred reading" \
1281		    "\`diff -e $1 $2\`: \$?=$?, err=" $err
1282		return
1283	fi
1284
1285	# Accumulate totals
1286	(( TOTL += tot ))
1287	(( TMOD += mod ))
1288	(( TDEL += del ))
1289	(( TINS += ins ))
1290	# Calculate unchanged lines
1291	wc -l $1 | read unc filename
1292	if (( unc > 0 )); then
1293		(( unc -= del + mod ))
1294		(( TUNC += unc ))
1295	fi
1296	# print summary
1297	print "<span class=\"lineschanged\">"
1298	printCI $tot $ins $del $mod $unc
1299	print "</span>"
1300}
1301
1302
1303#
1304# flist_from_wx
1305#
1306# Sets up webrev to source its information from a wx-formatted file.
1307# Sets the global 'wxfile' variable.
1308#
1309function flist_from_wx
1310{
1311	typeset argfile=$1
1312	if [[ -n ${argfile%%/*} ]]; then
1313		#
1314		# If the wx file pathname is relative then make it absolute
1315		# because the webrev does a "cd" later on.
1316		#
1317		wxfile=$PWD/$argfile
1318	else
1319		wxfile=$argfile
1320	fi
1321
1322	nawk '{ c = 1; print;
1323	  while (getline) {
1324		if (NF == 0) { c = -c; continue }
1325		if (c > 0) print
1326	  }
1327	}' $wxfile > $FLIST
1328
1329	print " Done."
1330}
1331
1332#
1333# flist_from_teamware [ <args-to-putback-n> ]
1334#
1335# Generate the file list by extracting file names from a putback -n.  Some
1336# names may come from the "update/create" messages and others from the
1337# "currently checked out" warning.  Renames are detected here too.  Extract
1338# values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
1339# -n as well, but remove them if they are already defined.
1340#
1341function flist_from_teamware
1342{
1343	if [[ -n $codemgr_parent ]]; then
1344		if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then
1345			print -u2 "parent $codemgr_parent doesn't look like a" \
1346			    "valid teamware workspace"
1347			exit 1
1348		fi
1349		parent_args="-p $codemgr_parent"
1350	fi
1351
1352	print " File list from: 'putback -n $parent_args $*' ... \c"
1353
1354	putback -n $parent_args $* 2>&1 |
1355	    nawk '
1356		/^update:|^create:/	{print $2}
1357		/^Parent workspace:/	{printf("CODEMGR_PARENT=%s\n",$3)}
1358		/^Child workspace:/	{printf("CODEMGR_WS=%s\n",$3)}
1359		/^The following files are currently checked out/ {p = 1; continue}
1360		NF == 0			{p=0 ; continue}
1361		/^rename/		{old=$3}
1362		$1 == "to:"		{print $2, old}
1363		/^"/			{continue}
1364		p == 1			{print $1}' |
1365	    sort -r -k 1,1 -u | sort > $FLIST
1366
1367	print " Done."
1368}
1369
1370function env_from_flist
1371{
1372	[[ -r $FLIST ]] || return
1373
1374	#
1375	# Use "eval" to set env variables that are listed in the file
1376	# list.  Then copy those into our local versions of those
1377	# variables if they have not been set already.
1378	#
1379	eval `sed -e "s/#.*$//" $FLIST | grep = `
1380
1381	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
1382
1383	#
1384	# Check to see if CODEMGR_PARENT is set in the flist file.
1385	#
1386	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
1387	    codemgr_parent=$CODEMGR_PARENT
1388}
1389
1390#
1391# detect_scm
1392#
1393# We dynamically test the SCM type; this allows future extensions to
1394# new SCM types
1395#
1396function detect_scm
1397{
1398	#
1399	# If CODEMGR_WS is specified in the flist file, we assume teamware.
1400	#
1401	if [[ -r $FLIST ]]; then
1402		egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
1403		if [[ $? -eq 0 ]]; then
1404			print "teamware"
1405			return
1406		fi
1407	fi
1408
1409	#
1410	# The presence of $CODEMGR_WS and a Codemgr_wsdata directory
1411	# is our clue that this is a teamware workspace.
1412	#
1413	if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
1414		print "teamware"
1415	else
1416		print "unknown"
1417	fi
1418}
1419
1420function look_for_prog
1421{
1422	typeset path
1423	typeset ppath
1424	typeset progname=$1
1425
1426	ppath=$PATH
1427	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1428	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
1429	ppath=$ppath:/opt/onbld/bin/`/usr/bin/uname -p`
1430
1431	PATH=$ppath prog=`whence $progname`
1432	if [[ -n $prog ]]; then
1433		print $prog
1434	fi
1435}
1436
1437#
1438# Usage message.
1439#
1440function usage
1441{
1442	print 'Usage:\twebrev [common-options]
1443	webrev [common-options] ( <file> | - )
1444	webrev [common-options] -w <wx file>
1445	webrev [common-options] -l [arguments to 'putback']
1446
1447Options:
1448	-O: Print bugids/arc cases suitable for OpenSolaris.
1449	-i <filename>: Include <filename> in the index.html file.
1450	-o <outdir>: Output webrev to specified directory.
1451	-p <compare-against>: Use specified parent wkspc or basis for comparison
1452	-w <wxfile>: Use specified wx active file.
1453
1454Environment:
1455	WDIR: Control the output directory.
1456	WEBREV_BUGURL: Control the URL prefix for bugids.
1457	WEBREV_SACURL: Control the URL prefix for ARC cases.
1458
1459SCM Environment:
1460	Teamware: CODEMGR_WS: Workspace location.
1461	Teamware: CODEMGR_PARENT: Parent workspace location.
1462'
1463
1464	exit 2
1465}
1466
1467#
1468#
1469# Main program starts here
1470#
1471#
1472
1473trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
1474
1475set +o noclobber
1476
1477[[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
1478[[ -z $WX ]] && WX=`look_for_prog wx`
1479[[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
1480[[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
1481[[ -z $PERL ]] && PERL=`look_for_prog perl`
1482
1483if [[ ! -x $PERL ]]; then
1484	print -u2 "Error: No perl interpreter found.  Exiting."
1485	exit 1
1486fi
1487
1488#
1489# These aren't fatal, but we want to note them to the user.
1490# We don't warn on the absence of 'wx' until later when we've
1491# determined that we actually need to try to invoke it.
1492#
1493[[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
1494[[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
1495[[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
1496
1497# Declare global total counters.
1498integer TOTL TINS TDEL TMOD TUNC
1499
1500flist_mode=
1501flist_file=
1502iflag=
1503oflag=
1504pflag=
1505lflag=
1506wflag=
1507Oflag=
1508while getopts "i:o:p:lwO" opt
1509do
1510	case $opt in
1511	i)	iflag=1
1512		INCLUDE_FILE=$OPTARG;;
1513
1514	o)	oflag=1
1515		WDIR=$OPTARG;;
1516
1517	p)	pflag=1
1518		codemgr_parent=$OPTARG;;
1519
1520	#
1521	# If -l has been specified, we need to abort further options
1522	# processing, because subsequent arguments are going to be
1523	# arguments to 'putback -n'.
1524	#
1525	l)	lflag=1
1526		break;;
1527
1528	w)	wflag=1;;
1529
1530	O)	Oflag=1;;
1531
1532	?)	usage;;
1533	esac
1534done
1535
1536FLIST=/tmp/$$.flist
1537
1538if [[ -n $wflag && -n $lflag ]]; then
1539	usage
1540fi
1541
1542#
1543# If this manually set as the parent, and it appears to be an earlier webrev,
1544# then note that fact and set the parent to the raw_files/new subdirectory.
1545#
1546if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
1547	parent_webrev="$codemgr_parent"
1548	codemgr_parent="$codemgr_parent/raw_files/new"
1549fi
1550
1551if [[ -z $wflag && -z $lflag ]]; then
1552	shift $(($OPTIND - 1))
1553
1554	if [[ $1 == "-" ]]; then
1555		cat > $FLIST
1556		flist_mode="stdin"
1557		flist_done=1
1558		shift
1559	elif [[ -n $1 ]]; then
1560		if [[ ! -r $1 ]]; then
1561			print -u2 "$1: no such file or not readable"
1562			usage
1563		fi
1564		cat $1 > $FLIST
1565		flist_mode="file"
1566		flist_file=$1
1567		flist_done=1
1568		shift
1569	else
1570		flist_mode="auto"
1571	fi
1572fi
1573
1574#
1575# Before we go on to further consider -l and -w, work out which SCM we think
1576# is in use.
1577#
1578SCM_MODE=`detect_scm $FLIST`
1579if [[ $SCM_MODE == "unknown" ]]; then
1580	print -u2 "Unable to determine SCM type currently in use."
1581	print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
1582	print -u2 "              the environment or in the file list."
1583	exit 1
1584fi
1585
1586print -u2 "   SCM detected: $SCM_MODE"
1587
1588if [[ -n $lflag ]]; then
1589	#
1590	# If the -l flag is given instead of the name of a file list,
1591	# then generate the file list by extracting file names from a
1592	# putback -n.
1593	#
1594	shift $(($OPTIND - 1))
1595	flist_from_teamware "$*"
1596	flist_done=1
1597	shift $#
1598
1599elif [[ -n $wflag ]]; then
1600	#
1601	# If the -w is given then assume the file list is in Bonwick's "wx"
1602	# command format, i.e.  pathname lines alternating with SCCS comment
1603	# lines with blank lines as separators.  Use the SCCS comments later
1604	# in building the index.html file.
1605	#
1606	shift $(($OPTIND - 1))
1607	wxfile=$1
1608	if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
1609		if [[ -r $CODEMGR_WS/wx/active ]]; then
1610			wxfile=$CODEMGR_WS/wx/active
1611		fi
1612	fi
1613
1614	[[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
1615	    "be auto-detected (check \$CODEMGR_WS)" && exit 1
1616
1617	print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
1618	flist_from_wx $wxfile
1619	flist_done=1
1620	if [[ -n "$*" ]]; then
1621		shift
1622	fi
1623elif [[ $flist_mode == "stdin" ]]; then
1624	print -u2 " File list from: standard input"
1625elif [[ $flist_mode == "file" ]]; then
1626	print -u2 " File list from: $flist_file"
1627fi
1628
1629if [[ $# -gt 0 ]]; then
1630	print -u2 "WARNING: unused arguments: $*"
1631fi
1632
1633if [[ $SCM_MODE == "teamware" ]]; then
1634	#
1635	# Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
1636	# be set in a number of ways, in decreasing precedence:
1637	#
1638	#      1) on the command line (only for the parent)
1639	#      2) in the user environment
1640	#      3) in the flist
1641	#      4) automatically based on the workspace (only for the parent)
1642	#
1643
1644	#
1645	# Here is case (2): the user environment
1646	#
1647	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
1648	if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
1649		print -u2 "$codemgr_ws: no such workspace"
1650		exit 1
1651	fi
1652
1653	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
1654	    codemgr_parent=$CODEMGR_PARENT
1655	if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
1656		print -u2 "$codemgr_parent: no such directory"
1657		exit 1
1658	fi
1659
1660	#
1661	# If we're in auto-detect mode and we haven't already gotten the file
1662	# list, then see if we can get it by probing for wx.
1663	#
1664	if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
1665		if [[ ! -x $WX ]]; then
1666			print -u2 "WARNING: wx not found!"
1667		fi
1668
1669		#
1670		# We need to use wx list -w so that we get renamed files, etc.
1671		# but only if a wx active file exists-- otherwise wx will
1672		# hang asking us to initialize our wx information.
1673		#
1674		if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
1675			print -u2 " File list from: 'wx list -w' ... \c"
1676			$WX list -w > $FLIST
1677			$WX comments > /tmp/$$.wx_comments
1678			wxfile=/tmp/$$.wx_comments
1679			print -u2 "done"
1680			flist_done=1
1681		fi
1682	fi
1683
1684	#
1685	# If by hook or by crook we've gotten a file list by now (perhaps
1686	# from the command line), eval it to extract environment variables from
1687	# it: This is step (3).
1688	#
1689	env_from_flist
1690
1691	#
1692	# Continuing step (3): If we still have no file list, we'll try to get
1693	# it from teamware.
1694	#
1695	if [[ -z $flist_done ]]; then
1696		flist_from_teamware
1697		env_from_flist
1698	fi
1699
1700	#
1701	# Observe true directory name of CODEMGR_WS, as used later in
1702	# webrev title.
1703	#
1704	codemgr_ws=$(cd $codemgr_ws;print $PWD)
1705
1706	#
1707	# (4) If we still don't have a value for codemgr_parent, get it
1708	# from workspace.
1709	#
1710	[[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent`
1711	if [[ ! -d $codemgr_parent ]]; then
1712		print -u2 "$CODEMGR_PARENT: no such parent workspace"
1713		exit 1
1714	fi
1715
1716	#
1717	# Reset CODEMGR_WS to make sure teamware commands are happy.
1718	#
1719	CODEMGR_WS=$codemgr_ws
1720	CWS=$codemgr_ws
1721	PWS=$codemgr_parent
1722fi
1723
1724#
1725# If the user didn't specify a -i option, check to see if there is a
1726# webrev-info file in the workspace directory.
1727#
1728if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
1729	iflag=1
1730	INCLUDE_FILE="$CWS/webrev-info"
1731fi
1732
1733if [[ -n $iflag ]]; then
1734	if [[ ! -r $INCLUDE_FILE ]]; then
1735		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
1736		    "not readable."
1737		exit 1
1738	else
1739		#
1740		# $INCLUDE_FILE may be a relative path, and the script alters
1741		# PWD, so we just stash a copy in /tmp.
1742		#
1743		cp $INCLUDE_FILE /tmp/$$.include
1744	fi
1745fi
1746
1747#
1748# Output directory.
1749#
1750WDIR=${WDIR:-$CWS/webrev}
1751
1752#
1753# Name of the webrev, derived from the workspace name; in the
1754# future this could potentially be an option.
1755#
1756WNAME=${CWS##*/}
1757
1758if [ "${WDIR%%/*}" ]; then
1759	WDIR=$PWD/$WDIR
1760fi
1761
1762if [[ ! -d $WDIR ]]; then
1763	mkdir -p $WDIR
1764	[[ $? != 0 ]] && exit 1
1765fi
1766
1767#
1768# Summarize what we're going to do.
1769#
1770print "      Workspace: $CWS"
1771if [[ -n $parent_webrev ]]; then
1772	print "Compare against: webrev at $parent_webrev"
1773else
1774	print "Compare against: $PWS"
1775fi
1776
1777[[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
1778print "      Output to: $WDIR"
1779
1780#
1781# Save the file list in the webrev dir
1782#
1783[[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
1784
1785#
1786#    Bug IDs will be replaced by a URL.  Order of precedence
1787#    is: default location, $WEBREV_BUGURL, the -O flag.
1788#
1789BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr='
1790[[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
1791[[ -n "$Oflag" ]] && \
1792    BUGURL='http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id='
1793
1794#
1795#    Likewise, ARC cases will be replaced by a URL.  Order of precedence
1796#    is: default, $WEBREV_SACURL, the -O flag.
1797#
1798#    Note that -O also triggers different substitution behavior for
1799#    SACURL.  See sac2url().
1800#
1801SACURL='http://sac.eng.sun.com'
1802[[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
1803[[ -n "$Oflag" ]] && \
1804    SACURL='http://www.opensolaris.org/os/community/arc/caselog'
1805
1806rm -f $WDIR/$WNAME.patch
1807rm -f $WDIR/$WNAME.ps
1808rm -f $WDIR/$WNAME.pdf
1809
1810touch $WDIR/$WNAME.patch
1811
1812print "   Output Files:"
1813
1814#
1815# Clean up the file list: Remove comments, blank lines and env variables.
1816#
1817sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
1818FLIST=/tmp/$$.flist.clean
1819
1820#
1821# First pass through the files: generate the per-file webrev HTML-files.
1822#
1823cat $FLIST | while read LINE
1824do
1825	set - $LINE
1826	P=$1
1827
1828	#
1829	# Normally, each line in the file list is just a pathname of a
1830	# file that has been modified or created in the child.  A file
1831	# that is renamed in the child workspace has two names on the
1832	# line: new name followed by the old name.
1833	#
1834	oldname=""
1835	oldpath=""
1836	rename=
1837	if [[ $# -eq 2 ]]; then
1838		PP=$2			# old filename
1839		oldname=" (was $PP)"
1840		oldpath="$PP"
1841		rename=1
1842        	PDIR=${PP%/*}
1843        	if [[ $PDIR == $PP ]]; then
1844			PDIR="."   # File at root of workspace
1845		fi
1846
1847		PF=${PP##*/}
1848
1849	        DIR=${P%/*}
1850	        if [[ $DIR == $P ]]; then
1851			DIR="."   # File at root of workspace
1852		fi
1853
1854		F=${P##*/}
1855
1856        else
1857	        DIR=${P%/*}
1858	        if [[ "$DIR" == "$P" ]]; then
1859			DIR="."   # File at root of workspace
1860		fi
1861
1862		F=${P##*/}
1863
1864		PP=$P
1865		PDIR=$DIR
1866		PF=$F
1867	fi
1868
1869	COMM=`getcomments html $P $PP`
1870
1871	if [[ ! -d $CWS/$DIR ]]; then
1872		print "  $CWS/$DIR: no such directory"
1873		continue
1874	fi
1875
1876	print "\t$P$oldname\n\t\t\c"
1877
1878	# Make the webrev mirror directory if necessary
1879	mkdir -p $WDIR/$DIR
1880
1881	# cd to the directory so the names are short
1882	cd $CWS/$DIR
1883
1884	#
1885	# If we're in OpenSolaris mode, we enforce a minor policy:
1886	# help to make sure the reviewer doesn't accidentally publish
1887	# source which is in usr/closed/* or deleted_files/usr/closed/*
1888	#
1889	if [[ -n "$Oflag" ]]; then
1890		pclosed=${P##usr/closed/}
1891		pdeleted=${P##deleted_files/usr/closed/}
1892		if [[ "$pclosed" != "$P" || "$pdeleted" != "$P" ]]; then
1893			print "*** Omitting closed source for OpenSolaris" \
1894			    "mode review"
1895			continue
1896		fi
1897	fi
1898
1899	#
1900	# We stash old and new files into parallel directories in /tmp
1901	# and do our diffs there.  This makes it possible to generate
1902	# clean looking diffs which don't have absolute paths present.
1903	#
1904	olddir=$WDIR/raw_files/old
1905	newdir=$WDIR/raw_files/new
1906	mkdir -p $olddir
1907	mkdir -p $newdir
1908	mkdir -p $olddir/$PDIR
1909	mkdir -p $newdir/$DIR
1910
1911	if [[ $SCM_MODE == "teamware" ]]; then
1912		# If the child's version doesn't exist then
1913		# get a readonly copy.
1914
1915		if [[ ! -f $F && -f SCCS/s.$F ]]; then
1916			sccs get -s $F
1917		fi
1918
1919		#
1920		# Snag new version of file.
1921		#
1922		rm -f $newdir/$DIR/$F
1923		cp $F $newdir/$DIR/$F
1924
1925		#
1926		# Get the parent's version of the file. First see whether the
1927		# child's version is checked out and get the parent's version
1928		# with keywords expanded or unexpanded as appropriate.
1929		#
1930		if [ -f "$PWS/$PDIR/SCCS/s.$PF" -o \
1931		    -f "$PWS/$PDIR/SCCS/p.$PF" ]; then
1932			rm -f $olddir/$PDIR/$PF
1933			if [ -f "SCCS/p.$F" ]; then
1934				sccs get -s -p -k $PWS/$PDIR/$PF \
1935				    > $olddir/$PDIR/$PF
1936			else
1937				sccs get -s -p    $PWS/$PDIR/$PF \
1938				    > $olddir/$PDIR/$PF
1939			fi
1940		else
1941			if [[ -f $PWS/$PDIR/$PF ]]; then
1942				# Parent is not a real workspace, but just a raw
1943				# directory tree - use the file that's there as
1944				# the old file.
1945
1946				rm -f $olddir/$DIR/$F
1947				cp $PWS/$PDIR/$PF $olddir/$DIR/$F
1948			fi
1949		fi
1950	fi
1951
1952	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
1953		print "*** Error: file not in parent or child"
1954		continue
1955	fi
1956
1957	cd $WDIR/raw_files
1958	ofile=old/$PDIR/$PF
1959	nfile=new/$DIR/$F
1960
1961	mv_but_nodiff=
1962	cmp $ofile $nfile > /dev/null 2>&1
1963	if [[ $? == 0 && $rename == 1 ]]; then
1964		mv_but_nodiff=1
1965	fi
1966
1967	#
1968	# If we have old and new versions of the file then run the appropriate
1969	# diffs.  This is complicated by a couple of factors:
1970	#
1971	#	- renames must be handled specially: we emit a 'remove'
1972	#	  diff and an 'add' diff
1973	#	- new files and deleted files must be handled specially
1974	#	- Solaris patch(1m) can't cope with file creation
1975	#	  (and hence renames) as of this writing.
1976	#       - To make matters worse, gnu patch doesn't interpret the
1977	#	  output of Solaris diff properly when it comes to
1978	#	  adds and deletes.  We need to do some "cleansing"
1979	#         transformations:
1980	# 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
1981	#	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
1982	#
1983	cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
1984	cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
1985
1986	rm -f $WDIR/$DIR/$F.patch
1987	if [[ -z $rename ]]; then
1988		if [ ! -f "$ofile" ]; then
1989			diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
1990			    > $WDIR/$DIR/$F.patch
1991		elif [ ! -f "$nfile" ]; then
1992			diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
1993			    > $WDIR/$DIR/$F.patch
1994		else
1995			diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
1996		fi
1997	else
1998		diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
1999		    > $WDIR/$DIR/$F.patch
2000
2001		diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
2002		    >> $WDIR/$DIR/$F.patch
2003
2004	fi
2005
2006	#
2007	# Tack the patch we just made onto the accumulated patch for the
2008	# whole wad.
2009	#
2010	cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
2011
2012	print " patch\c"
2013
2014	if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
2015
2016		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
2017		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
2018		    > $WDIR/$DIR/$F.cdiff.html
2019		print " cdiffs\c"
2020
2021		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
2022		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
2023		    > $WDIR/$DIR/$F.udiff.html
2024
2025		print " udiffs\c"
2026
2027		if [[ -x $WDIFF ]]; then
2028			$WDIFF -c "$COMM" \
2029			    -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
2030			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
2031			if [[ $? -eq 0 ]]; then
2032				print " wdiffs\c"
2033			else
2034				print " wdiffs[fail]\c"
2035			fi
2036		fi
2037
2038		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
2039		    > $WDIR/$DIR/$F.sdiff.html
2040		print " sdiffs\c"
2041
2042		print " frames\c"
2043
2044		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
2045
2046		difflines $ofile $nfile > $WDIR/$DIR/$F.count
2047
2048	elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
2049		# renamed file: may also have differences
2050		difflines $ofile $nfile > $WDIR/$DIR/$F.count
2051	elif [[ -f $nfile ]]; then
2052		# new file: count added lines
2053		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
2054	elif [[ -f $ofile ]]; then
2055		# old file: count deleted lines
2056		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
2057	fi
2058
2059	#
2060	# Now we generate the postscript for this file.  We generate diffs
2061	# only in the event that there is delta, or the file is new (it seems
2062	# tree-killing to print out the contents of deleted files).
2063	#
2064	if [[ -f $nfile ]]; then
2065		ocr=$ofile
2066		[[ ! -f $ofile ]] && ocr=/dev/null
2067
2068		if [[ -z $mv_but_nodiff ]]; then
2069			textcomm=`getcomments text $P $PP`
2070			if [[ -x $CODEREVIEW ]]; then
2071				$CODEREVIEW -y "$textcomm" \
2072				    -e $ocr $nfile \
2073				    > /tmp/$$.psfile 2>/dev/null &&
2074				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
2075				if [[ $? -eq 0 ]]; then
2076					print " ps\c"
2077				else
2078					print " ps[fail]\c"
2079				fi
2080			fi
2081		fi
2082	fi
2083
2084	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
2085		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
2086		print " old\c"
2087	fi
2088
2089	if [[ -f $nfile ]]; then
2090		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
2091		print " new\c"
2092	fi
2093
2094	print
2095done
2096
2097frame_nav_js > $WDIR/ancnav.js
2098frame_navigation > $WDIR/ancnav.html
2099
2100if [[ ! -f $WDIR/$WNAME.ps ]]; then
2101	print " Generating PDF: Skipped: no output available"
2102elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then
2103	print " Generating PDF: \c"
2104	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
2105	rm -f $WDIR/$WNAME.ps
2106	print "Done."
2107else
2108	print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'"
2109fi
2110
2111# If we're in OpenSolaris mode and there's a closed dir under $WDIR,
2112# delete it - prevent accidental publishing of closed source
2113
2114if [[ -n "$Oflag" ]]; then
2115	/usr/bin/find $WDIR -type d -name closed -exec /bin/rm -rf {} \;
2116fi
2117
2118# Now build the index.html file that contains
2119# links to the source files and their diffs.
2120
2121cd $CWS
2122
2123# Save total changed lines for Code Inspection.
2124print "$TOTL" > $WDIR/TotalChangedLines
2125
2126print "     index.html: \c"
2127INDEXFILE=$WDIR/index.html
2128exec 3<&1			# duplicate stdout to FD3.
2129exec 1<&-			# Close stdout.
2130exec > $INDEXFILE		# Open stdout to index file.
2131
2132print "$HTML<head>$STDHEAD"
2133print "<title>$WNAME</title>"
2134print "</head>"
2135print "<body id=\"SUNWwebrev\">"
2136print "<div class=\"summary\">"
2137print "<h2>Code Review for $WNAME</h2>"
2138
2139print "<table>"
2140
2141#
2142# Figure out the username and gcos name.  To maintain compatibility
2143# with passwd(4), we must support '&' substitutions.
2144#
2145username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
2146realname=`getent passwd $username | cut -d':' -f 5`
2147userupper=`$PERL -e "print ucfirst $username"`
2148realname=`print $realname | sed s/\&/$userupper/`
2149date="on `date`"
2150
2151if [[ -n "$username" && -n "$realname" ]]; then
2152	print "<tr><th>Prepared by:</th>"
2153	print "<td>$realname ($username) $date</td></tr>"
2154elif [[ -n "$username" ]]; then
2155	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
2156fi
2157
2158print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
2159print "<tr><th>Compare against:</th><td>"
2160if [[ -n $parent_webrev ]]; then
2161	print "webrev at $parent_webrev"
2162else
2163	print "$PWS"
2164fi
2165print "</td></tr>"
2166print "<tr><th>Summary of changes:</th><td>"
2167printCI $TOTL $TINS $TDEL $TMOD $TUNC
2168print "</td></tr>"
2169
2170if [[ -f $WDIR/$WNAME.patch ]]; then
2171	print "<tr><th>Patch of changes:</th><td>"
2172	print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
2173fi
2174if [[ -f $WDIR/$WNAME.pdf ]]; then
2175	print "<tr><th>Printable review:</th><td>"
2176	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
2177fi
2178
2179if [[ -n "$iflag" ]]; then
2180	print "<tr><th>Author comments:</th><td><div>"
2181	cat /tmp/$$.include
2182	print "</div></td></tr>"
2183fi
2184print "</table>"
2185print "</div>"
2186
2187
2188#
2189# Second pass through the files: generate the rest of the index file
2190#
2191cat $FLIST | while read LINE
2192do
2193	set - $LINE
2194	P=$1
2195
2196	if [[ $# == 2 ]]; then
2197		PP=$2
2198		oldname=" <i>(was $PP)</i>"
2199
2200	else
2201		PP=$P
2202		oldname=""
2203	fi
2204
2205	DIR=${P%/*}
2206	if [[ $DIR == $P ]]; then
2207		DIR="."   # File at root of workspace
2208	fi
2209
2210	# Avoid processing the same file twice.
2211	# It's possible for renamed files to
2212	# appear twice in the file list
2213
2214	F=$WDIR/$P
2215
2216	print "<p>"
2217
2218	# If there's a diffs file, make diffs links
2219
2220	if [[ -f $F.cdiff.html ]]; then
2221		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
2222		print "<a href=\"$P.udiff.html\">Udiffs</a>"
2223
2224		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
2225			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
2226		fi
2227
2228		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
2229
2230		print "<a href=\"$P.frames.html\">Frames</a>"
2231	else
2232		print " ------ ------ ------"
2233
2234		if [[ -x $WDIFF ]]; then
2235			print " ------"
2236		fi
2237
2238		print " ------"
2239	fi
2240
2241	# If there's an old file, make the link
2242
2243	if [[ -f $F-.html ]]; then
2244		print "<a href=\"$P-.html\">Old</a>"
2245	else
2246		print " ---"
2247	fi
2248
2249	# If there's an new file, make the link
2250
2251	if [[ -f $F.html ]]; then
2252		print "<a href=\"$P.html\">New</a>"
2253	else
2254		print " ---"
2255	fi
2256
2257	if [[ -f $F.patch ]]; then
2258		print "<a href=\"$P.patch\">Patch</a>"
2259	else
2260		print " -----"
2261	fi
2262
2263	if [[ -f $WDIR/raw_files/new/$P ]]; then
2264		print "<a href=\"raw_files/new/$P\">Raw</a>"
2265	else
2266		print " ---"
2267	fi
2268
2269	print "<b>$P</b> $oldname"
2270
2271	#
2272	# Check for usr/closed and deleted_files/usr/closed
2273	#
2274	if [ ! -z "$Oflag" ]; then
2275		if [[ $P == usr/closed/* || \
2276		    $P == deleted_files/usr/closed/* ]]; then
2277			print "&nbsp;&nbsp;<i>Closed source: omitted from" \
2278			    "this review</i>"
2279		fi
2280	fi
2281
2282	print "</p>"
2283	# Insert delta comments
2284
2285	print "<blockquote><pre>"
2286	getcomments html $P $PP
2287	print "</pre>"
2288
2289	# Add additional comments comment
2290
2291	print "<!-- Add comments to explain changes in $P here -->"
2292
2293	# Add count of changes.
2294
2295	if [[ -f $F.count ]]; then
2296	    cat $F.count
2297	    rm $F.count
2298	fi
2299	print "</blockquote>"
2300done
2301
2302print
2303print
2304print "<hr></hr>"
2305print "<p style=\"font-size: small\">"
2306print "This code review page was prepared using <b>$0</b>"
2307print "(vers $WEBREV_UPDATED)."
2308print "Webrev is maintained by the <a href=\"http://www.opensolaris.org\">"
2309print "OpenSolaris</a> project.  The latest version may be obtained"
2310print "<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>"
2311print "</body>"
2312print "</html>"
2313
2314exec 1<&-			# Close FD 1.
2315exec 1<&3			# dup FD 3 to restore stdout.
2316exec 3<&-			# close FD 3.
2317
2318print "Done."
2319