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.
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.
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]
24 # Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
27 # Copyright 2008, 2010, Richard Lowe
30 # This script takes a file list and a workspace and builds a set of html files
31 # suitable for doing a code review of source changes via a web page.
32 # Documentation is available via the manual page, webrev.1, or just
35 # Acknowledgements to contributors to webrev are listed in the webrev(1)
43 HTML
='<?xml version="1.0"?>
44 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
45 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
46 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
48 FRAMEHTML
='<?xml version="1.0"?>
49 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
50 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
51 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
53 STDHEAD
='<meta http-equiv="cache-control" content="no-cache"></meta>
54 <meta http-equiv="Pragma" content="no-cache"></meta>
55 <meta http-equiv="Expires" content="-1"></meta>
57 Note to customizers: the body of the webrev is IDed as SUNWwebrev
58 to allow easy overriding by users of webrev via the userContent.css
59 mechanism available in some browsers.
61 For example, to have all "removed" information be red instead of
62 brown, set a rule in your userContent.css file like:
64 body#SUNWwebrev span.removed { color: red ! important; }
66 <style type="text/css" media="screen">
68 background-color: #eeeeee;
72 border-top: 1px solid #aaa;
77 border-bottom: 1px solid #aaa;
84 div.summary table th {
116 a.print { font-size: x-small; }
117 a:hover { background-color: #ffcc99; }
120 <style type="text/css" media="print">
121 pre { font-size: 0.8em; font-family: courier, monospace; }
122 span.removed { color: #444; font-style: italic }
123 span.changed { font-weight: bold; }
124 span.new { font-weight: bold; }
125 span.newmarker { font-size: 1.2em; font-weight: bold; }
126 span.oldmarker { font-size: 1.2em; font-weight: bold; }
127 a.print {display: none}
128 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
133 # UDiffs need a slightly different CSS rule for 'new' items (we don't
134 # want them to be bolded as we do in cdiffs or sdiffs).
137 <style type="text/css" media="screen">
146 # Display remote target with prefix and trailing slash.
148 function print_upload_header
151 typeset display_target
153 if [[ -z $tflag ]]; then
154 display_target
=${prefix}${remote_target}
156 display_target
=${remote_target}
159 if [[ ${display_target} != */ ]]; then
160 display_target
=${display_target}/
163 print
" Upload to: ${display_target}\n" \
168 # Upload the webrev via rsync. Return 0 on success, 1 on error.
170 function rsync_upload
172 if (( $# != 2 )); then
173 print
"\nERROR: rsync_upload: wrong usage ($#)"
178 integer
-r print_err_msg
=$2
180 print_upload_header
${rsync_prefix}
182 typeset
-r err_msg
=$
( $MKTEMP /tmp
/rsync_err.XXXXXX
)
183 if [[ -z $err_msg ]]; then
184 print
"\nERROR: rsync_upload: cannot create temporary file"
188 # The source directory must end with a slash in order to copy just
189 # directory contents, not the whole directory.
191 typeset src_dir
=$WDIR
192 if [[ ${src_dir} != */ ]]; then
195 $RSYNC -r -q ${src_dir} $dst 2>$err_msg
196 if (( $?
!= 0 )); then
197 if (( ${print_err_msg} > 0 )); then
198 print
"Failed.\nERROR: rsync failed"
199 print
"src dir: '${src_dir}'\ndst dir: '$dst'"
200 print
"error messages:"
201 $SED 's/^/> /' $err_msg
213 # Create directories on remote host using SFTP. Return 0 on success,
216 function remote_mkdirs
218 typeset
-r dir_spec
=$1
219 typeset
-r host_spec
=$2
222 # If the supplied path is absolute we assume all directories are
223 # created, otherwise try to create all directories in the path
224 # except the last one which will be created by scp.
226 if [[ "${dir_spec}" == */* && "${dir_spec}" != /* ]]; then
229 # Remove the last directory from directory specification.
231 typeset
-r dirs_mk
=${dir_spec%/*}
232 typeset
-r batch_file_mkdir
=$
( $MKTEMP \
233 /tmp
/webrev_mkdir.XXXXXX
)
234 if [[ -z $batch_file_mkdir ]]; then
235 print
"\nERROR: remote_mkdirs:" \
236 "cannot create temporary file for batch file"
242 for dir
in ${dirs_mk}; do
244 # Use the '-' prefix to ignore mkdir errors in order
245 # to avoid an error in case the directory already
246 # exists. We check the directory with chdir to be sure
249 print
-- "-mkdir ${dir}" >> ${batch_file_mkdir}
250 print
"chdir ${dir}" >> ${batch_file_mkdir}
253 typeset
-r sftp_err_msg
=$
( $MKTEMP /tmp
/webrev_scp_err.XXXXXX
)
254 if [[ -z ${sftp_err_msg} ]]; then
255 print
"\nERROR: remote_mkdirs:" \
256 "cannot create temporary file for error messages"
259 $SFTP -b ${batch_file_mkdir} ${host_spec} 2>${sftp_err_msg} 1>&2
260 if (( $?
!= 0 )); then
261 print
"\nERROR: failed to create remote directories"
262 print
"error messages:"
263 $SED 's/^/> /' ${sftp_err_msg}
264 rm -f ${sftp_err_msg} ${batch_file_mkdir}
267 rm -f ${sftp_err_msg} ${batch_file_mkdir}
274 # Upload the webrev via SSH. Return 0 on success, 1 on error.
278 if (( $# != 1 )); then
279 print
"\nERROR: ssh_upload: wrong number of arguments"
284 typeset
-r host_spec
=${dst%%:*}
285 typeset
-r dir_spec
=${dst#*:}
288 # Display the upload information before calling delete_webrev
289 # because it will also print its progress.
291 print_upload_header
${ssh_prefix}
294 # If the deletion was explicitly requested there is no need
295 # to perform it again.
297 if [[ -z $Dflag ]]; then
299 # We do not care about return value because this might be
300 # the first time this directory is uploaded.
306 # Create remote directories. Any error reporting will be done
307 # in remote_mkdirs function.
309 remote_mkdirs
${dir_spec} ${host_spec}
310 if (( $?
!= 0 )); then
314 print
"upload ... \c"
315 typeset
-r scp_err_msg
=$
( $MKTEMP /tmp
/scp_err.XXXXXX
)
316 if [[ -z ${scp_err_msg} ]]; then
317 print
"\nERROR: ssh_upload:" \
318 "cannot create temporary file for error messages"
321 $SCP -q -C -B -o PreferredAuthentications
=publickey
-r \
322 $WDIR $dst 2>${scp_err_msg}
323 if (( $?
!= 0 )); then
324 print
"Failed.\nERROR: scp failed"
325 print
"src dir: '$WDIR'\ndst dir: '$dst'"
326 print
"error messages:"
327 $SED 's/^/> /' ${scp_err_msg}
338 # Delete webrev at remote site. Return 0 on success, 1 or exit code from sftp
339 # on failure. If first argument is 1 then perform the check of sftp return
340 # value otherwise ignore it. If second argument is present it means this run
341 # only performs deletion.
343 function delete_webrev
345 if (( $# < 1 )); then
346 print
"delete_webrev: wrong number of arguments"
351 integer delete_only
=0
352 if (( $# == 2 )); then
357 # Strip the transport specification part of remote target first.
359 typeset
-r stripped_target
=${remote_target##*://}
360 typeset
-r host_spec
=${stripped_target%%:*}
361 typeset
-r dir_spec
=${stripped_target#*:}
365 # Do not accept an absolute path.
367 if [[ ${dir_spec} == /* ]]; then
372 # Strip the ending slash.
374 if [[ ${dir_spec} == */ ]]; then
375 dir_rm
=${dir_spec%%/}
380 if (( ${delete_only} > 0 )); then
381 print
" Removing: \c"
385 if [[ -z "$dir_rm" ]]; then
386 print
"\nERROR: empty directory for removal"
391 # Prepare batch file.
393 typeset
-r batch_file_rm
=$
( $MKTEMP /tmp
/webrev_remove.XXXXXX
)
394 if [[ -z $batch_file_rm ]]; then
395 print
"\nERROR: delete_webrev: cannot create temporary file"
398 print
"rename $dir_rm $TRASH_DIR/removed.$$" > $batch_file_rm
401 # Perform remote deletion and remove the batch file.
403 typeset
-r sftp_err_msg
=$
( $MKTEMP /tmp
/webrev_scp_err.XXXXXX
)
404 if [[ -z ${sftp_err_msg} ]]; then
405 print
"\nERROR: delete_webrev:" \
406 "cannot create temporary file for error messages"
409 $SFTP -b $batch_file_rm $host_spec 2>${sftp_err_msg} 1>&2
412 if (( $ret != 0 && $check > 0 )); then
413 print
"Failed.\nERROR: failed to remove remote directories"
414 print
"error messages:"
415 $SED 's/^/> /' ${sftp_err_msg}
416 rm -f ${sftp_err_msg}
419 rm -f ${sftp_err_msg}
420 if (( ${delete_only} > 0 )); then
428 # Upload webrev to remote site
430 function upload_webrev
434 if [[ ! -d "$WDIR" ]]; then
435 print
"\nERROR: webrev directory '$WDIR' does not exist"
440 # Perform a late check to make sure we do not upload closed source
441 # to remote target when -n is used. If the user used custom remote
442 # target he probably knows what he is doing.
444 if [[ -n $nflag && -z $tflag ]]; then
445 $FIND $WDIR -type d
-name closed \
446 |
$GREP closed
>/dev
/null
447 if (( $?
== 0 )); then
448 print
"\nERROR: directory '$WDIR' contains" \
449 "\"closed\" directory"
456 # We have the URI for remote destination now so let's start the upload.
458 if [[ -n $tflag ]]; then
459 if [[ "${remote_target}" == ${rsync_prefix}?
* ]]; then
460 rsync_upload
${remote_target##$rsync_prefix} 1
463 elif [[ "${remote_target}" == ${ssh_prefix}?
* ]]; then
464 ssh_upload
${remote_target##$ssh_prefix}
470 # Try rsync first and fallback to SSH in case it fails.
472 rsync_upload
${remote_target} 0
474 if (( $ret != 0 )); then
475 print
"Failed. (falling back to SSH)"
476 ssh_upload
${remote_target}
484 # input_cmd | url_encode | output_cmd
486 # URL-encode (percent-encode) reserved characters as defined in RFC 3986.
488 # Reserved characters are: :/?#[]@!$&'()*+,;=
490 # While not a reserved character itself, percent '%' is reserved by definition
491 # so encode it first to avoid recursive transformation, and skip '/' which is
494 # The quotation character is deliberately not escaped in order to make
495 # the substitution work with GNU sed.
499 $SED -e "s|%|%25|g" -e "s|:|%3A|g" -e "s|\&|%26|g" \
500 -e "s|?|%3F|g" -e "s|#|%23|g" -e "s|\[|%5B|g" \
501 -e "s|*|%2A|g" -e "s|@|%40|g" -e "s|\!|%21|g" \
502 -e "s|=|%3D|g" -e "s|;|%3B|g" -e "s|\]|%5D|g" \
503 -e "s|(|%28|g" -e "s|)|%29|g" -e "s|'|%27|g" \
504 -e "s|+|%2B|g" -e "s|\,|%2C|g" -e "s|\\\$|%24|g"
508 # input_cmd | html_quote | output_cmd
510 # html_quote filename | output_cmd
512 # Make a piece of source code safe for display in an HTML <pre> block.
516 $SED -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" "$@" |
expand
520 # input_cmd | its2url | output_cmd
522 # Scan for information tracking system references and insert <a> links to the
523 # relevant databases.
527 $SED -f ${its_sed_script}
531 # strip_unchanged <infile> | output_cmd
533 # Removes chunks of sdiff documents that have not changed. This makes it
534 # easier for a code reviewer to find the bits that have changed.
536 # Deleted lines of text are replaced by a horizontal rule. Some
537 # identical lines are retained before and after the changed lines to
538 # provide some context. The number of these lines is controlled by the
539 # variable C in the $AWK script below.
541 # The script detects changed lines as any line that has a "<span class="
542 # string embedded (unchanged lines have no particular class and are not
543 # part of a <span>). Blank lines (without a sequence number) are also
544 # detected since they flag lines that have been inserted or deleted.
550 NF == 0 || /<span class="/ {
555 print "\n</pre><hr></hr><pre>"
560 for (i = 0; i < c; i++)
561 print ln[(inx + i) % C]
575 END { if (c > (C * 2)) print "\n</pre><hr></hr>" }
583 # This function takes two files as arguments, obtains their diff, and
584 # processes the diff output to present the files as an HTML document with
585 # the files displayed side-by-side, differences shown in color. It also
586 # takes a delta comment, rendered as an HTML snippet, as the third
587 # argument. The function takes two files as arguments, then the name of
588 # file, the path, and the comment. The HTML will be delivered on stdout,
591 # $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
592 # new/usr/src/tools/scripts/webrev.sh \
593 # webrev.sh usr/src/tools/scripts \
594 # '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
595 # 1234567</a> my bugid' > <file>.html
597 # framed_sdiff() is then called which creates $2.frames.html
598 # in the webrev tree.
600 # FYI: This function is rather unusual in its use of awk. The initial
601 # diff run produces conventional diff output showing changed lines mixed
602 # with editing codes. The changed lines are ignored - we're interested in
603 # the editing codes, e.g.
612 # These editing codes are parsed by the awk script and used to generate
613 # another awk script that generates HTML, e.g the above lines would turn
614 # into something like this:
616 # BEGIN { printf "<pre>\n" }
617 # function sp(n) {for (i=0;i<n;i++)printf "\n"}
618 # function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
619 # NR==8 {wl("#7A7ADD");next}
620 # NR==54 {wl("#7A7ADD");sp(3);next}
621 # NR==56 {wl("#7A7ADD");next}
622 # NR==57 {wl("black");printf "\n"; next}
625 # This script is then run on the original source file to generate the
626 # HTML that corresponds to the source file.
628 # The two HTML files are then combined into a single piece of HTML that
629 # uses an HTML table construct to present the files side by side. You'll
630 # notice that the changes are color-coded:
632 # black - unchanged lines
633 # blue - changed lines
634 # bold blue - new lines
635 # brown - deleted lines
637 # Blank lines are inserted in each file to keep unchanged lines in sync
638 # (side-by-side). This format is familiar to users of sdiff(1) or
639 # Teamware's filemerge tool.
643 diff -b $1 $2 > /tmp
/$$.diffs
650 # Now we have the diffs, generate the HTML for the old file.
654 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
655 printf "function removed() "
656 printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
657 printf "function changed() "
658 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
659 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
666 split($1, a, /[cad]/) ;
667 if (index($1, "a")) {
669 n = split(a[2], r, /,/);
671 printf "BEGIN\t\t{sp(1)}\n"
673 printf "BEGIN\t\t{sp(%d)}\n",\
678 printf "NR==%s\t\t{", a[1]
679 n = split(a[2], r, /,/);
682 printf "bl();printf \"\\n\"; next}\n"
685 printf "bl();sp(%d);next}\n",\
690 if (index($1, "d")) {
691 n = split(a[1], r, /,/);
695 printf "NR==%s\t\t{removed(); next}\n" , n1
697 printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
700 if (index($1, "c")) {
701 n = split(a[1], r, /,/);
707 printf "NR==%s\t\t{changed();" , n1
710 printf "NR==%s,NR==%s\t{changed();" , n1, n2
712 m = split(a[2], r, /,/);
718 if (n > 1) printf "if (NR==%d)", final
719 printf "sp(%d);", d2 - d1
728 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
729 ' /tmp
/$$.diffs
> /tmp
/$$.file1
732 # Now generate the HTML for the new file
736 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
737 printf "function new() "
738 printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
739 printf "function changed() "
740 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
741 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
749 split($1, a, /[cad]/) ;
750 if (index($1, "d")) {
752 n = split(a[1], r, /,/);
754 printf "BEGIN\t\t{sp(1)}\n"
756 printf "BEGIN\t\t{sp(%d)}\n",\
761 printf "NR==%s\t\t{", a[2]
762 n = split(a[1], r, /,/);
765 printf "bl();printf \"\\n\"; next}\n"
768 printf "bl();sp(%d);next}\n",\
773 if (index($1, "a")) {
774 n = split(a[2], r, /,/);
778 printf "NR==%s\t\t{new() ; next}\n" , n1
780 printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
783 if (index($1, "c")) {
784 n = split(a[2], r, /,/);
791 printf "NR==%s\t\t{changed();" , n1
794 printf "NR==%s,NR==%s\t{changed();" , n1, n2
796 m = split(a[1], r, /,/);
802 if (n > 1) printf "if (NR==%d)", final
803 printf "sp(%d);", d1 - d2
810 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
811 ' /tmp
/$$.diffs
> /tmp
/$$.file2
814 # Post-process the HTML files by running them back through $AWK
816 html_quote
< $1 |
$AWK -f /tmp
/$$.file1
> /tmp
/$$.file1.html
818 html_quote
< $2 |
$AWK -f /tmp
/$$.file2
> /tmp
/$$.file2.html
821 # Now combine into a valid HTML file and side-by-side into a table
823 print
"$HTML<head>$STDHEAD"
824 print
"<title>$WNAME Sdiff $TPATH/$TNAME</title>"
825 print
"</head><body id=\"SUNWwebrev\">"
826 print
"<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
827 print
"<pre>$COMMENT</pre>\n"
828 print
"<table><tr valign=\"top\">"
831 strip_unchanged
/tmp
/$$.file1.html
833 print
"</pre></td><td><pre>"
835 strip_unchanged
/tmp
/$$.file2.html
838 print
"</tr></table>"
839 print
"</body></html>"
841 framed_sdiff
$TNAME $TPATH /tmp
/$$.file1.html
/tmp
/$$.file2.html \
847 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
849 # Expects lefthand and righthand side html files created by sdiff_to_html.
850 # We use insert_anchors() to augment those with HTML navigation anchors,
851 # and then emit the main frame. Content is placed into:
853 # $WDIR/DIR/$TNAME.lhs.html
854 # $WDIR/DIR/$TNAME.rhs.html
855 # $WDIR/DIR/$TNAME.frames.html
857 # NOTE: We rely on standard usage of $WDIR and $DIR.
859 function framed_sdiff
868 # Enable html files to access WDIR via a relative path.
869 RTOP
=$
(relative_dir
$TPATH $WDIR)
871 # Make the rhs/lhs files and output the frameset file.
872 print
"$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
874 cat >> $WDIR/$DIR/$TNAME.lhs.html
<<-EOF
875 <script type="text/javascript" src="${RTOP}ancnav.js"></script>
877 <body id="SUNWwebrev" onkeypress="keypress(event);">
879 <pre>$comments</pre><hr></hr>
882 cp $WDIR/$DIR/$TNAME.lhs.html
$WDIR/$DIR/$TNAME.rhs.html
884 insert_anchors
$lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
885 insert_anchors
$rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
887 close
='</body></html>'
889 print
$close >> $WDIR/$DIR/$TNAME.lhs.html
890 print
$close >> $WDIR/$DIR/$TNAME.rhs.html
892 print
"$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
893 print
"<title>$WNAME Framed-Sdiff " \
894 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
895 cat >> $WDIR/$DIR/$TNAME.frames.html
<<-EOF
896 <frameset rows="*,60">
897 <frameset cols="50%,50%">
898 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs"></frame>
899 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame>
901 <frame src="${RTOP}ancnav.html" scrolling="no" marginwidth="0"
902 marginheight="0" name="nav"></frame>
904 <body id="SUNWwebrev">
905 Alas 'frames' webrev requires that your browser supports frames
906 and has the feature enabled.
918 # Merge codereview output files to a single conforming postscript file, by:
919 # - removing all extraneous headers/trailers
920 # - making the page numbers right
921 # - removing pages devoid of contents which confuse some
922 # postscript readers.
926 function fix_postscript
930 cat > /tmp
/$$.crmerge.pl
<< \EOF
932 print scalar
(<>); # %!PS-Adobe---
933 print
"%%Orientation: Landscape\n";
941 next
if (/^
%%Pages
:\s
*\d
+/);
944 if ($pno == 0 ||
$page =~
/\
)S
/) {
945 # Header or single page containing text
946 print
"%%Page: ? $pno\n" if ($pno > 0);
950 # Empty page, skip it.
957 # Skip from %%Trailer of one document to Endprolog
959 $doprint = 0 if (/^
%%Trailer
/);
960 $page .
= $_ if ($doprint);
963 if ($page =~
/\
)S
/) {
964 print
"%%Page: ? $pno\n";
969 print
"%%Trailer\n%%Pages: $pno\n";
972 $PERL /tmp
/$$.crmerge.pl
< $infile
977 # input_cmd | insert_anchors | output_cmd
979 # Flag blocks of difference with sequentially numbered invisible
980 # anchors. These are used to drive the frames version of the
983 # NOTE: Anchor zero flags the top of the file irrespective of changes,
984 # an additional anchor is also appended to flag the bottom.
986 # The script detects changed lines as any line that has a "<span
987 # class=" string embedded (unchanged lines have no class set and are
988 # not part of a <span>. Blank lines (without a sequence number)
989 # are also detected since they flag lines that have been inserted or
992 function insert_anchors
996 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
1004 NF == 0 || /^<span class=/ {
1019 printf "<b style=\"font-size: large; color: red\">";
1020 printf "--- EOF ---</b>"
1021 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
1023 printf "<form name=\"eof\">";
1024 printf "<input name=\"value\" value=\"%d\" " \
1025 "type=\"hidden\"></input>", anc - 1;
1035 # Print a relative return path from $1 to $2. For example if
1036 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
1037 # this function would print "../../../../".
1039 # In the event that $1 is not in $2 a warning is printed to stderr,
1040 # and $2 is returned-- the result of this is that the resulting webrev
1041 # is not relocatable.
1043 function relative_dir
1045 typeset cur
="${1##$2?(/)}"
1048 # If the first path was specified absolutely, and it does
1049 # not start with the second path, it's an error.
1051 if [[ "$cur" = "/${1#/}" ]]; then
1052 # Should never happen.
1053 print
-u2 "\nWARNING: relative_dir: \"$1\" not relative "
1054 print
-u2 "to \"$2\". Check input paths. Framed webrev "
1055 print
-u2 "will not be relocatable!"
1061 # This is kind of ugly. The sed script will do the following:
1063 # 1. Strip off a leading "." or "./": this is important to get
1064 # the correct arcnav links for files in $WDIR.
1065 # 2. Strip off a trailing "/": this is not strictly necessary,
1066 # but is kind of nice, since it doesn't end up in "//" at
1067 # the end of a relative path.
1068 # 3. Replace all remaining sequences of non-"/" with "..": the
1069 # assumption here is that each dirname represents another
1070 # level of relative separation.
1071 # 4. Append a trailing "/" only for non-empty paths: this way
1072 # the caller doesn't need to duplicate this logic, and does
1073 # not end up using $RTOP/file for files in $WDIR.
1075 print
$cur |
$SED -e '{
1086 # Emit javascript for frame navigation
1088 function frame_nav_js
1096 function scrollByPix
() {
1101 parent.lhs.scrollBy
(0,sfactor
);
1102 parent.rhs.scrollBy
(0,sfactor
);
1106 function scrollToAnc
(num
) {
1108 // Update the value of the anchor
in the form
which we use as
1109 // storage
for this value. setAncValue
() will take care of
1110 // correcting
for overflow and underflow of the value and
return
1111 // us the new value.
1112 num
= setAncValue
(num
);
1114 // Set location and scroll back a little to expose previous
1117 // Note that this could be improved
: it is possible although
1118 // complex to compute the x and y position of an anchor
, and to
1119 // scroll to that location directly.
1121 parent.lhs.location.replace
(parent.lhs.location.pathname
+ "#" + num
);
1122 parent.rhs.location.replace
(parent.rhs.location.pathname
+ "#" + num
);
1124 parent.lhs.scrollBy
(0,-30);
1125 parent.rhs.scrollBy
(0,-30);
1128 function getAncValue
()
1130 return (parseInt
(parent.nav.document.
diff.real.value
));
1133 function setAncValue
(val
)
1137 parent.nav.document.
diff.real.value
= val
;
1138 parent.nav.document.
diff.display.value
= "BOF";
1143 // The way we compute the max anchor value is to stash it
1144 // inline
in the left and right hand side pages-- it
's the same
1145 // on each side, so we pluck from the left.
1147 maxval = parent.lhs.document.eof.value.value;
1149 parent.nav.document.diff.real.value = val;
1150 parent.nav.document.diff.display.value = val.toString();
1154 // this must be: val >= maxval
1156 parent.nav.document.diff.real.value = val;
1157 parent.nav.document.diff.display.value = "EOF";
1161 function stopScroll() {
1163 clearInterval(myInt);
1168 function startScroll() {
1171 myInt=setInterval("scrollByPix()",10);
1174 function handlePress(b) {
1181 scrollToAnc(getAncValue() - 1);
1192 scrollToAnc(getAncValue() + 1);
1195 scrollToAnc(999999);
1200 function handleRelease(b) {
1204 function keypress(ev) {
1208 if (window.event) { // IE
1209 keynum = ev.keyCode;
1210 } else if (ev.which) { // non-IE
1214 keychar = String.fromCharCode(keynum);
1216 if (keychar == "k") {
1219 } else if (keychar == "j" || keychar == " ") {
1226 function ValidateDiffNum(){
1227 val = parent.nav.document.diff.display.value;
1229 scrollToAnc(999999);
1240 parent.nav.document.diff.display.value = getAncValue();
1253 # Output anchor navigation file for framed sdiffs.
1255 function frame_navigation
1257 print "$HTML<head>$STDHEAD"
1260 <title>Anchor Navigation</title>
1261 <meta http-equiv="Content-Script-Type" content="text/javascript">
1262 <meta http-equiv="Content-Type" content="text/html">
1264 <style type="text/css">
1265 div.button td { padding-left: 5px; padding-right: 5px;
1266 background-color: #eee; text-align: center;
1267 border: 1px #444 outset; cursor: pointer; }
1268 div.button a { font-weight: bold; color: black }
1269 div.button td:hover { background: #ffcc99; }
1273 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
1277 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
1278 onkeypress="keypress(event);">
1279 <noscript lang="javascript">
1281 <p><big>Framed Navigation controls require Javascript</big><br></br>
1282 Either this browser is incompatable or javascript is not enabled</p>
1285 <table width="100%" border="0" align="center">
1287 <td valign="middle" width="25%">Diff navigation:
1288 Use 'j
' and 'k
' for next and previous diffs; or use buttons
1290 <td align="center" valign="top" width="50%">
1291 <div class="button">
1292 <table border="0" align="center">
1295 <a onMouseDown="handlePress(1);return true;"
1296 onMouseUp="handleRelease(1);return true;"
1297 onMouseOut="handleRelease(1);return true;"
1298 onClick="return false;"
1299 title="Go to Beginning Of file">BOF</a></td>
1301 <a onMouseDown="handlePress(3);return true;"
1302 onMouseUp="handleRelease(3);return true;"
1303 onMouseOut="handleRelease(3);return true;"
1304 title="Scroll Up: Press and Hold to accelerate"
1305 onClick="return false;">Scroll Up</a></td>
1307 <a onMouseDown="handlePress(2);return true;"
1308 onMouseUp="handleRelease(2);return true;"
1309 onMouseOut="handleRelease(2);return true;"
1310 title="Go to previous Diff"
1311 onClick="return false;">Prev Diff</a>
1316 <a onMouseDown="handlePress(6);return true;"
1317 onMouseUp="handleRelease(6);return true;"
1318 onMouseOut="handleRelease(6);return true;"
1319 onClick="return false;"
1320 title="Go to End Of File">EOF</a></td>
1322 <a onMouseDown="handlePress(4);return true;"
1323 onMouseUp="handleRelease(4);return true;"
1324 onMouseOut="handleRelease(4);return true;"
1325 title="Scroll Down: Press and Hold to accelerate"
1326 onClick="return false;">Scroll Down</a></td>
1328 <a onMouseDown="handlePress(5);return true;"
1329 onMouseUp="handleRelease(5);return true;"
1330 onMouseOut="handleRelease(5);return true;"
1331 title="Go to next Diff"
1332 onClick="return false;">Next Diff</a></td>
1337 <th valign="middle" width="25%">
1338 <form action="" name="diff" onsubmit="return ValidateDiffNum();">
1339 <input name="display" value="BOF" size="8" type="text"></input>
1340 <input name="real" value="0" size="8" type="hidden"></input>
1353 # diff_to_html <filename> <filepath> { U | C } <comment>
1355 # Processes the output of diff to produce an HTML file representing either
1356 # context or unified diffs.
1365 print "$HTML<head>$STDHEAD"
1366 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
1368 if [[ $DIFFTYPE == "U" ]]; then
1374 <body id="SUNWwebrev">
1375 <a class="print" href="javascript:print()">Print this page</a>
1382 /^\
+\
+\
+ new
/ { next
}
1384 /^\
*\
*\
* old
/ { next
}
1385 /^\
*\
*\
*\
*/ { next
}
1386 /^
-------/ { printf "<center><h1>%s</h1></center>\n", $0; next
}
1387 /^\@\@.
*\@\@$
/ { printf "</pre><hr></hr><pre>\n";
1388 printf "<span class=\"newmarker\">%s</span>\n", $0;
1391 /^\
*\
*\
*/ { printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0;
1393 /^
---/ { printf "<span class=\"newmarker\">%s</span>\n", $0;
1395 /^\
+/ {printf "<span class=\"new\">%s</span>\n", $0; next
}
1396 /^
!/ {printf "<span class=\"changed\">%s</span>\n", $0; next
}
1397 /^
-/ {printf "<span class=\"removed\">%s</span>\n", $0; next
}
1398 {printf "%s\n", $0; next
}
1401 print "</pre></body></html>\n"
1406 # source_to_html { new | old } <filename>
1408 # Process a plain vanilla source file to transform it into an HTML file.
1415 print "$HTML<head>$STDHEAD"
1416 print "<title>$WNAME $WHICH $TNAME</title>"
1417 print "<body id=\"SUNWwebrev\">"
1419 html_quote | $AWK '{line
+= 1 ; printf "%4d %s\n", line
, $0 }'
1420 print "</pre></body></html>"
1424 # comments_from_teamware {text|html} parent-file child-file
1426 # Find the first delta in the child that's not
in the parent. Get the
1427 # newest delta from the parent, get all deltas from the child starting
1428 # with that delta, and then get all info starting with the second oldest
1429 # delta in that list (the first delta unique to the child).
1431 # This code adapted from Bill Shannon's "spc" script
1433 comments_from_teamware
()
1439 if [[ ! -f $PWS/${2%/*}/SCCS
/s.
${2##*/} && -n $RWS ]]; then
1443 if [[ -f $pfile ]]; then
1444 psid
=$
($SCCS prs
-d:I
: $pfile 2>/dev
/null
)
1449 set -A sids $
($SCCS prs
-l -r$psid -d:I
: $cfile 2>/dev
/null
)
1453 /^COMMENTS:/ {p=1; continue}
1454 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
1455 NF == 0u { continue }
1456 {if (p==0) continue; print $0 }'
1458 if [[ $N -ge 2 ]]; then
1459 sid1
=${sids[$((N-2))]} # Gets 2nd to last sid
1461 if [[ $fmt == "text" ]]; then
1462 $SCCS prs
-l -r$sid1 $cfile 2>/dev
/null | \
1467 $SCCS prs
-l -r$sid1 $cfile 2>/dev
/null | \
1468 html_quote | its2url |
$AWK "$nawkprg"
1473 # comments_from_wx {text|html} filepath
1475 # Given the pathname of a file, find its location in a "wx" active
1476 # file list and print the following comment. Output is either text or
1477 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1478 # are turned into URLs.
1480 # This is also used with Mercurial and the file list provided by hg-active.
1489 do getline ; while (NF > 0)
1491 while (NF > 0) { print ; getline }
1495 if [[ -z $comm ]]; then
1496 comm="*** NO COMMENTS ***"
1499 if [[ $fmt == "text" ]]; then
1504 print
-- "$comm" | html_quote | its2url
1509 # getcomments {text|html} filepath parentpath
1511 # Fetch the comments depending on what SCM mode we're in.
1519 if [[ -n $Nflag ]]; then
1523 # Mercurial support uses a file list in wx format, so this
1524 # will be used there, too
1526 if [[ -n $wxfile ]]; then
1527 comments_from_wx
$fmt $p
1529 if [[ $SCM_MODE == "teamware" ]]; then
1530 comments_from_teamware
$fmt $pp $p
1536 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1538 # Print out Code Inspection figures similar to sccs-prt(1) format.
1542 integer tot
=$1 ins
=$2 del
=$3 mod
=$4 unc
=$5
1544 if (( tot
== 1 )); then
1549 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \
1550 $tot $str $ins $del $mod $unc
1555 # difflines <oldfile> <newfile>
1557 # Calculate and emit number of added, removed, modified and unchanged lines,
1558 # and total lines changed, the sum of added + removed + modified.
1562 integer tot mod del ins unc err
1565 eval $
( diff -e $1 $2 |
$AWK '
1566 # Change range of lines: N,Nc
1567 /^[0-9]*,[0-9]*c$/ {
1568 n=split(substr($1,1,length($1)-1), counts, ",");
1574 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
1575 # following would be 5 - 3 = 2! Hence +1 for correction.
1577 r=(counts[2]-counts[1])+1;
1580 # Now count replacement lines: each represents a change instead
1581 # of a delete, so increment c and decrement r.
1583 while (getline != /^\.$/) {
1588 # If there were more replacement lines than original lines,
1589 # then r will be negative; in this case there are no deletions,
1590 # but there are r changes that should be counted as adds, and
1591 # since r is negative, subtract it from a and add it to c.
1599 # If there were more original lines than replacement lines, then
1600 # r will be positive; in this case, increment d by that much.
1610 # The first line is a replacement; any more are additions.
1611 if (getline != /^\.$/) {
1613 while (getline != /^\.$/) a++;
1618 # Add lines: both Na and N,Na
1620 while (getline != /^\.$/) a++;
1624 # Delete range of lines: N,Nd
1625 /^[0-9]*,[0-9]*d$/ {
1626 n=split(substr($1,1,length($1)-1), counts, ",");
1632 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
1633 # following would be 5 - 3 = 2! Hence +1 for correction.
1635 r=(counts[2]-counts[1])+1;
1640 # Delete line: Nd. For example 10d says line 10 is deleted.
1641 /^[0-9]*d$/ {d++; next}
1643 # Should not get here!
1649 # Finish off - print results
1651 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
1652 (c+d+a), c, d, a, error);
1655 # End of $AWK, Check to see if any trouble occurred.
1656 if (( $?
> 0 || err
> 0 )); then
1657 print
"Unexpected Error occurred reading" \
1658 "\`diff -e $1 $2\`: \$?=$?, err=" $err
1667 # Calculate unchanged lines
1669 if (( unc
> 0 )); then
1670 (( unc
-= del
+ mod
))
1674 print
"<span class=\"lineschanged\">"
1675 printCI
$tot $ins $del $mod $unc
1683 # Sets up webrev to source its information from a wx-formatted file.
1684 # Sets the global 'wxfile' variable.
1686 function flist_from_wx
1689 if [[ -n ${argfile%%/*} ]]; then
1691 # If the wx file pathname is relative then make it absolute
1692 # because the webrev does a "cd" later on.
1694 wxfile
=$PWD/$argfile
1699 $AWK '{ c = 1; print;
1701 if (NF == 0) { c = -c; continue }
1710 # flist_from_teamware [ <args-to-putback-n> ]
1712 # Generate the file list by extracting file names from a putback -n. Some
1713 # names may come from the "update/create" messages and others from the
1714 # "currently checked out" warning. Renames are detected here too. Extract
1715 # values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
1716 # -n as well, but remove them if they are already defined.
1718 function flist_from_teamware
1720 if [[ -n $codemgr_parent && -z $parent_webrev ]]; then
1721 if [[ ! -d $codemgr_parent/Codemgr_wsdata
]]; then
1722 print
-u2 "parent $codemgr_parent doesn't look like a" \
1723 "valid teamware workspace"
1726 parent_args
="-p $codemgr_parent"
1729 print
" File list from: 'putback -n $parent_args $*' ... \c"
1731 putback
-n $parent_args $
* 2>&1 |
1733 /^update:|^create:/ {print $2}
1734 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)}
1735 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)}
1736 /^The following files are currently checked out/ {p = 1; continue}
1737 NF == 0 {p=0 ; continue}
1739 $1 == "to:" {print $2, old}
1741 p == 1 {print $1}' |
1742 sort -r -k 1,1 -u |
sort > $FLIST
1748 # Call hg-active to get the active list output in the wx active list format
1750 function hg_active_wxfile
1755 TMPFLIST
=/tmp
/$$.active
1756 $HG_ACTIVE -w $child -p $parent -o $TMPFLIST
1761 # flist_from_mercurial
1762 # Call hg-active to get a wx-style active list, and hand it off to
1765 function flist_from_mercurial
1770 print
" File list from: hg-active -p $parent ...\c"
1771 if [[ ! -x $HG_ACTIVE ]]; then
1772 print
# Blank line for the \c above
1773 print
-u2 "Error: hg-active tool not found. Exiting"
1776 hg_active_wxfile
$child $parent
1778 # flist_from_wx prints the Done, so we don't have to.
1779 flist_from_wx
$TMPFLIST
1783 # flist_from_subversion
1785 # Generate the file list by extracting file names from svn status.
1787 function flist_from_subversion
1793 print
-u2 " File list from: svn status ... \c"
1794 svn status |
$AWK '/^[ACDMR]/ { print $NF }' > $FLIST
1799 function env_from_flist
1801 [[ -r $FLIST ]] ||
return
1804 # Use "eval" to set env variables that are listed in the file
1805 # list. Then copy those into our local versions of those
1806 # variables if they have not been set already.
1808 eval `$SED -e "s/#.*$//" $FLIST | $GREP = `
1810 if [[ -z $codemgr_ws && -n $CODEMGR_WS ]]; then
1811 codemgr_ws
=$CODEMGR_WS
1816 # Check to see if CODEMGR_PARENT is set in the flist file.
1818 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
1819 codemgr_parent
=$CODEMGR_PARENT
1820 export CODEMGR_PARENT
1824 function look_for_prog
1831 ppath
=$ppath:/usr
/sfw
/bin
:/usr
/bin
:/usr
/sbin
1832 ppath
=$ppath:/opt
/teamware
/bin
:/opt
/onbld
/bin
1833 ppath
=$ppath:/opt
/onbld
/bin
/`uname -p`
1835 PATH
=$ppath prog
=`whence $progname`
1836 if [[ -n $prog ]]; then
1841 function get_file_mode
1844 if (@stat = stat($ARGV[0])) {
1845 $mode = $stat[2] & 0777;
1846 printf "%03o\n", $mode;
1854 function build_old_new_teamware
1859 # If the child's version doesn't exist then
1860 # get a readonly copy.
1862 if [[ ! -f $CWS/$DIR/$F && -f $CWS/$DIR/SCCS
/s.
$F ]]; then
1863 $SCCS get
-s -p $CWS/$DIR/$F > $CWS/$DIR/$F
1866 # The following two sections propagate file permissions the
1867 # same way SCCS does. If the file is already under version
1868 # control, always use permissions from the SCCS/s.file. If
1869 # the file is not under SCCS control, use permissions from the
1870 # working copy. In all cases, the file copied to the webrev
1871 # is set to read only, and group/other permissions are set to
1872 # match those of the file owner. This way, even if the file
1873 # is currently checked out, the webrev will display the final
1874 # permissions that would result after check in.
1877 # Snag new version of file.
1879 rm -f $newdir/$DIR/$F
1880 cp $CWS/$DIR/$F $newdir/$DIR/$F
1881 if [[ -f $CWS/$DIR/SCCS
/s.
$F ]]; then
1882 chmod `get_file_mode $CWS/$DIR/SCCS/s.$F` \
1885 chmod u-w
,go
=u
$newdir/$DIR/$F
1888 # Get the parent's version of the file. First see whether the
1889 # child's version is checked out and get the parent's version
1890 # with keywords expanded or unexpanded as appropriate.
1892 if [[ -f $PWS/$PDIR/$PF && ! -f $PWS/$PDIR/SCCS
/s.
$PF && \
1893 ! -f $PWS/$PDIR/SCCS
/p.
$PF ]]; then
1894 # Parent is not a real workspace, but just a raw
1895 # directory tree - use the file that's there as
1898 rm -f $olddir/$PDIR/$PF
1899 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
1901 if [[ -f $PWS/$PDIR/SCCS
/s.
$PF ]]; then
1907 rm -f $olddir/$PDIR/$PF
1909 if [[ -f $real_parent/$PDIR/$PF ]]; then
1910 if [ -f $CWS/$DIR/SCCS
/p.
$F ]; then
1911 $SCCS get
-s -p -k $real_parent/$PDIR/$PF > \
1914 $SCCS get
-s -p $real_parent/$PDIR/$PF > \
1917 chmod `get_file_mode $real_parent/$PDIR/SCCS/s.$PF` \
1921 if [[ -f $olddir/$PDIR/$PF ]]; then
1922 chmod u-w
,go
=u
$olddir/$PDIR/$PF
1926 function build_old_new_mercurial
1935 # Get old file mode, from the parent revision manifest entry.
1936 # Mercurial only stores a "file is executable" flag, but the
1937 # manifest will display an octal mode "644" or "755".
1939 if [[ "$PDIR" == "." ]]; then
1944 file=`echo $file | $SED 's#/#\\\/#g'`
1945 # match the exact filename, and return only the permission digits
1946 old_mode
=`$SED -n -e "/^\\(...\\) . ${file}$/s//\\1/p" \
1947 < $HG_PARENT_MANIFEST`
1950 # Get new file mode, directly from the filesystem.
1951 # Normalize the mode to match Mercurial's behavior.
1953 new_mode
=`get_file_mode $CWS/$DIR/$F`
1954 if [[ -n "$new_mode" ]]; then
1955 if [[ "$new_mode" = *[1357]* ]]; then
1963 # new version of the file.
1965 rm -rf $newdir/$DIR/$F
1966 if [[ -e $CWS/$DIR/$F ]]; then
1967 cp $CWS/$DIR/$F $newdir/$DIR/$F
1968 if [[ -n $new_mode ]]; then
1969 chmod $new_mode $newdir/$DIR/$F
1971 # should never happen
1972 print
-u2 "ERROR: set mode of $newdir/$DIR/$F"
1977 # parent's version of the file
1979 # Note that we get this from the last version common to both
1980 # ourselves and the parent. References are via $CWS since we have no
1981 # guarantee that the parent workspace is reachable via the filesystem.
1983 if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then
1984 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
1985 elif [[ -n $HG_PARENT ]]; then
1986 hg
cat -R $CWS -r $HG_PARENT $CWS/$PDIR/$PF > \
1987 $olddir/$PDIR/$PF 2>/dev
/null
1989 if (( $?
!= 0 )); then
1990 rm -f $olddir/$PDIR/$PF
1992 if [[ -n $old_mode ]]; then
1993 chmod $old_mode $olddir/$PDIR/$PF
1995 # should never happen
1996 print
-u2 "ERROR: set mode of $olddir/$PDIR/$PF"
2002 function build_old_new_subversion
2007 # Snag new version of file.
2008 rm -f $newdir/$DIR/$F
2009 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F
2011 if [[ -n $PWS && -e $PWS/$PDIR/$PF ]]; then
2012 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2014 # Get the parent's version of the file.
2015 svn status
$CWS/$DIR/$F |
read stat
file
2016 if [[ $stat != "A" ]]; then
2017 svn
cat -r BASE
$CWS/$DIR/$F > $olddir/$PDIR/$PF
2022 function build_old_new_unknown
2028 # Snag new version of file.
2030 rm -f $newdir/$DIR/$F
2031 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F
2034 # Snag the parent's version of the file.
2036 if [[ -f $PWS/$PDIR/$PF ]]; then
2037 rm -f $olddir/$PDIR/$PF
2038 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2042 function build_old_new
2052 typeset olddir
="$WDIR/raw_files/old"
2053 typeset newdir
="$WDIR/raw_files/new"
2055 mkdir
-p $olddir/$PDIR
2056 mkdir
-p $newdir/$DIR
2058 if [[ $SCM_MODE == "teamware" ]]; then
2059 build_old_new_teamware
"$olddir" "$newdir"
2060 elif [[ $SCM_MODE == "mercurial" ]]; then
2061 build_old_new_mercurial
"$olddir" "$newdir"
2062 elif [[ $SCM_MODE == "subversion" ]]; then
2063 build_old_new_subversion
"$olddir" "$newdir"
2064 elif [[ $SCM_MODE == "unknown" ]]; then
2065 build_old_new_unknown
"$olddir" "$newdir"
2068 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2069 print
"*** Error: file not in parent or child"
2081 print
'Usage:\twebrev [common-options]
2082 webrev [common-options] ( <file> | - )
2083 webrev [common-options] -w <wx file>
2086 -C <filename>: Use <filename> for the information tracking configuration.
2087 -D: delete remote webrev
2088 -i <filename>: Include <filename> in the index.html file.
2089 -I <filename>: Use <filename> for the information tracking registry.
2090 -n: do not generate the webrev (useful with -U)
2091 -O: Print bugids/arc cases suitable for OpenSolaris.
2092 -o <outdir>: Output webrev to specified directory.
2093 -p <compare-against>: Use specified parent wkspc or basis for comparison
2094 -t <remote_target>: Specify remote destination for webrev upload
2095 -U: upload the webrev to remote destination
2096 -w <wxfile>: Use specified wx active file.
2099 WDIR: Control the output directory.
2100 WEBREV_TRASH_DIR: Set directory for webrev delete.
2102 SCM Specific Options:
2103 TeamWare: webrev [common-options] -l [arguments to 'putback
']
2106 CODEMGR_WS: Workspace location.
2107 CODEMGR_PARENT: Parent workspace location.
2115 # Main program starts here
2119 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2123 PATH
=$
(dirname $
(whence
$0)):$PATH
2125 [[ -z $WDIFF ]] && WDIFF
=`look_for_prog wdiff`
2126 [[ -z $WX ]] && WX
=`look_for_prog wx`
2127 [[ -z $HG_ACTIVE ]] && HG_ACTIVE
=`look_for_prog hg-active`
2128 [[ -z $WHICH_SCM ]] && WHICH_SCM
=`look_for_prog which_scm`
2129 [[ -z $CODEREVIEW ]] && CODEREVIEW
=`look_for_prog codereview`
2130 [[ -z $PS2PDF ]] && PS2PDF
=`look_for_prog ps2pdf`
2131 [[ -z $PERL ]] && PERL
=`look_for_prog perl`
2132 [[ -z $RSYNC ]] && RSYNC
=`look_for_prog rsync`
2133 [[ -z $SCCS ]] && SCCS
=`look_for_prog sccs`
2134 [[ -z $AWK ]] && AWK
=`look_for_prog nawk`
2135 [[ -z $AWK ]] && AWK
=`look_for_prog gawk`
2136 [[ -z $AWK ]] && AWK
=`look_for_prog awk`
2137 [[ -z $SCP ]] && SCP
=`look_for_prog scp`
2138 [[ -z $SED ]] && SED
=`look_for_prog sed`
2139 [[ -z $SFTP ]] && SFTP
=`look_for_prog sftp`
2140 [[ -z $SORT ]] && SORT
=`look_for_prog sort`
2141 [[ -z $MKTEMP ]] && MKTEMP
=`look_for_prog mktemp`
2142 [[ -z $GREP ]] && GREP
=`look_for_prog grep`
2143 [[ -z $FIND ]] && FIND
=`look_for_prog find`
2145 # set name of trash directory for remote webrev deletion
2147 [[ -n $WEBREV_TRASH_DIR ]] && TRASH_DIR
=$WEBREV_TRASH_DIR
2149 if [[ ! -x $PERL ]]; then
2150 print
-u2 "Error: No perl interpreter found. Exiting."
2154 if [[ ! -x $WHICH_SCM ]]; then
2155 print
-u2 "Error: Could not find which_scm. Exiting."
2160 # These aren't fatal, but we want to note them to the user.
2161 # We don't warn on the absence of 'wx' until later when we've
2162 # determined that we actually need to try to invoke it.
2164 [[ ! -x $CODEREVIEW ]] && print
-u2 "WARNING: codereview(1) not found."
2165 [[ ! -x $PS2PDF ]] && print
-u2 "WARNING: ps2pdf(1) not found."
2166 [[ ! -x $WDIFF ]] && print
-u2 "WARNING: wdiff not found."
2168 # Declare global total counters.
2169 integer TOTL TINS TDEL TMOD TUNC
2171 # default remote host for upload/delete
2172 typeset
-r DEFAULT_REMOTE_HOST
="cr.opensolaris.org"
2173 # prefixes for upload targets
2174 typeset
-r rsync_prefix
="rsync://"
2175 typeset
-r ssh_prefix
="ssh://"
2196 # NOTE: when adding/removing options it is necessary to sync the list
2197 # with usr/src/tools/onbld/hgext/cdm.py
2199 while getopts "C:Di:I:lnNo:Op:t:Uw" opt
2208 INCLUDE_FILE
=$OPTARG;;
2214 # If -l has been specified, we need to abort further options
2215 # processing, because subsequent arguments are going to be
2216 # arguments to 'putback -n'.
2228 # Strip the trailing slash to correctly form remote target.
2232 codemgr_parent
=$OPTARG;;
2235 remote_target
=$OPTARG;;
2247 if [[ -n $wflag && -n $lflag ]]; then
2251 # more sanity checking
2252 if [[ -n $nflag && -z $Uflag ]]; then
2253 print
"it does not make sense to skip webrev generation" \
2258 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2259 echo "remote target has to be used only for upload or delete"
2264 # For the invocation "webrev -n -U" with no other options, webrev will assume
2265 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2266 # $(basename ${CWS}). So we need to get CWS set before we skip any remaining
2269 $WHICH_SCM |
read SCM_MODE junk ||
exit 1
2270 if [[ $SCM_MODE == "teamware" ]]; then
2272 # Teamware priorities:
2273 # 1. CODEMGR_WS from the environment
2276 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws
=$CODEMGR_WS
2277 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
2278 print
-u2 "$codemgr_ws: no such workspace"
2281 [[ -z $codemgr_ws ]] && codemgr_ws
=$
(workspace name
)
2282 codemgr_ws
=$
(cd $codemgr_ws;print
$PWD)
2283 CODEMGR_WS
=$codemgr_ws
2285 elif [[ $SCM_MODE == "mercurial" ]]; then
2287 # Mercurial priorities:
2288 # 1. hg root from CODEMGR_WS environment variable
2289 # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under
2290 # usr/closed when we run webrev
2291 # 2. hg root from directory of invocation
2293 if [[ ${PWD} =~
"usr/closed" ]]; then
2294 testparent
=${CODEMGR_WS}/usr
/closed
2295 # If we're in OpenSolaris mode, we enforce a minor policy:
2296 # help to make sure the reviewer doesn't accidentally publish
2297 # source which is under usr/closed
2298 if [[ -n "$Oflag" ]]; then
2299 print
-u2 "OpenSolaris output not permitted with" \
2300 "usr/closed changes"
2304 testparent
=${CODEMGR_WS}
2306 [[ -z $codemgr_ws && -n $testparent ]] && \
2307 codemgr_ws
=$
(hg root
-R $testparent 2>/dev
/null
)
2308 [[ -z $codemgr_ws ]] && codemgr_ws
=$
(hg root
2>/dev
/null
)
2310 elif [[ $SCM_MODE == "subversion" ]]; then
2312 # Subversion priorities:
2313 # 1. CODEMGR_WS from environment
2314 # 2. Relative path from current directory to SVN repository root
2316 if [[ -n $CODEMGR_WS && -d $CODEMGR_WS/.svn
]]; then
2319 svn info |
while read line
; do
2320 if [[ $line == "URL: "* ]]; then
2322 elif [[ $line == "Repository Root: "* ]]; then
2323 repo
=${line#Repository Root: }
2333 # If no SCM has been determined, take either the environment setting
2334 # setting for CODEMGR_WS, or the current directory if that wasn't set.
2336 if [[ -z ${CWS} ]]; then
2337 CWS
=${CODEMGR_WS:-.}
2341 # If the command line options indicate no webrev generation, either
2342 # explicitly (-n) or implicitly (-D but not -U), then there's a whole
2343 # ton of logic we can skip.
2345 # Instead of increasing indentation, we intentionally leave this loop
2346 # body open here, and exit via break from multiple points within.
2347 # Search for DO_EVERYTHING below to find the break points and closure.
2349 for do_everything
in 1; do
2351 # DO_EVERYTHING: break point
2352 if [[ -n $nflag ||
( -z $Uflag && -n $Dflag ) ]]; then
2357 # If this manually set as the parent, and it appears to be an earlier webrev,
2358 # then note that fact and set the parent to the raw_files/new subdirectory.
2360 if [[ -n $pflag && -d $codemgr_parent/raw_files
/new
]]; then
2361 parent_webrev
="$codemgr_parent"
2362 codemgr_parent
="$codemgr_parent/raw_files/new"
2365 if [[ -z $wflag && -z $lflag ]]; then
2366 shift $
(($OPTIND - 1))
2368 if [[ $1 == "-" ]]; then
2373 elif [[ -n $1 ]]; then
2374 if [[ ! -r $1 ]]; then
2375 print
-u2 "$1: no such file or not readable"
2389 # Before we go on to further consider -l and -w, work out which SCM we think
2393 teamware|mercurial|subversion
)
2396 if [[ $flist_mode == "auto" ]]; then
2397 print
-u2 "Unable to determine SCM in use and file list not specified"
2398 print
-u2 "See which_scm(1) for SCM detection information."
2403 if [[ $flist_mode == "auto" ]]; then
2404 print
-u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2410 print
-u2 " SCM detected: $SCM_MODE"
2412 if [[ -n $lflag ]]; then
2414 # If the -l flag is given instead of the name of a file list,
2415 # then generate the file list by extracting file names from a
2418 shift $
(($OPTIND - 1))
2419 if [[ $SCM_MODE == "teamware" ]]; then
2420 flist_from_teamware
"$*"
2422 print
-u2 -- "Error: -l option only applies to TeamWare"
2427 elif [[ -n $wflag ]]; then
2429 # If the -w is given then assume the file list is in Bonwick's "wx"
2430 # command format, i.e. pathname lines alternating with SCCS comment
2431 # lines with blank lines as separators. Use the SCCS comments later
2432 # in building the index.html file.
2434 shift $
(($OPTIND - 1))
2436 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2437 if [[ -r $CODEMGR_WS/wx
/active
]]; then
2438 wxfile
=$CODEMGR_WS/wx
/active
2442 [[ -z $wxfile ]] && print
-u2 "wx file not specified, and could not " \
2443 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2445 if [[ ! -r $wxfile ]]; then
2446 print
-u2 "$wxfile: no such file or not readable"
2450 print
-u2 " File list from: wx 'active' file '$wxfile' ... \c"
2451 flist_from_wx
$wxfile
2453 if [[ -n "$*" ]]; then
2456 elif [[ $flist_mode == "stdin" ]]; then
2457 print
-u2 " File list from: standard input"
2458 elif [[ $flist_mode == "file" ]]; then
2459 print
-u2 " File list from: $flist_file"
2462 if [[ $# -gt 0 ]]; then
2463 print
-u2 "WARNING: unused arguments: $*"
2467 # Before we entered the DO_EVERYTHING loop, we should have already set CWS
2468 # and CODEMGR_WS as needed. Here, we set the parent workspace.
2471 if [[ $SCM_MODE == "teamware" ]]; then
2474 # Teamware priorities:
2476 # 1) via -p command line option
2477 # 2) in the user environment
2479 # 4) automatically based on the workspace
2483 # For 1, codemgr_parent will already be set. Here's 2:
2485 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
2486 codemgr_parent
=$CODEMGR_PARENT
2487 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
2488 print
-u2 "$codemgr_parent: no such directory"
2493 # If we're in auto-detect mode and we haven't already gotten the file
2494 # list, then see if we can get it by probing for wx.
2496 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
2497 if [[ ! -x $WX ]]; then
2498 print
-u2 "WARNING: wx not found!"
2502 # We need to use wx list -w so that we get renamed files, etc.
2503 # but only if a wx active file exists-- otherwise wx will
2504 # hang asking us to initialize our wx information.
2506 if [[ -x $WX && -f $codemgr_ws/wx
/active
]]; then
2507 print
-u2 " File list from: 'wx list -w' ... \c"
2508 $WX list
-w > $FLIST
2509 $WX comments
> /tmp
/$$.wx_comments
2510 wxfile
=/tmp
/$$.wx_comments
2517 # If by hook or by crook we've gotten a file list by now (perhaps
2518 # from the command line), eval it to extract environment variables from
2519 # it: This is method 3 for finding the parent.
2521 if [[ -z $flist_done ]]; then
2527 # (4) If we still don't have a value for codemgr_parent, get it
2530 [[ -z $codemgr_parent ]] && codemgr_parent
=`workspace parent`
2531 if [[ ! -d $codemgr_parent ]]; then
2532 print
-u2 "$CODEMGR_PARENT: no such parent workspace"
2538 [[ -n $parent_webrev ]] && RWS
=$
(workspace parent
$CWS)
2540 elif [[ $SCM_MODE == "mercurial" ]]; then
2542 # Parent can either be specified with -p
2543 # Specified with CODEMGR_PARENT in the environment
2544 # or taken from hg's default path.
2547 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2548 codemgr_parent
=$CODEMGR_PARENT
2551 if [[ -z $codemgr_parent ]]; then
2552 codemgr_parent
=`hg path -R $codemgr_ws default 2>/dev/null`
2555 CWS_REV
=`hg parent -R $codemgr_ws --template '{node|short}' 2>/dev/null`
2559 # If the parent is a webrev, we want to do some things against
2560 # the natural workspace parent (file list, comments, etc)
2562 if [[ -n $parent_webrev ]]; then
2563 real_parent
=$
(hg path
-R $codemgr_ws default
2>/dev
/null
)
2569 # If hg-active exists, then we run it. In the case of no explicit
2570 # flist given, we'll use it for our comments. In the case of an
2571 # explicit flist given we'll try to use it for comments for any
2572 # files mentioned in the flist.
2574 if [[ -z $flist_done ]]; then
2575 flist_from_mercurial
$CWS $real_parent
2580 # If we have a file list now, pull out any variables set
2581 # therein. We do this now (rather than when we possibly use
2582 # hg-active to find comments) to avoid stomping specifications
2583 # in the user-specified flist.
2585 if [[ -n $flist_done ]]; then
2590 # Only call hg-active if we don't have a wx formatted file already
2592 if [[ -x $HG_ACTIVE && -z $wxfile ]]; then
2593 print
" Comments from: hg-active -p $real_parent ...\c"
2594 hg_active_wxfile
$CWS $real_parent
2599 # At this point we must have a wx flist either from hg-active,
2600 # or in general. Use it to try and find our parent revision,
2601 # if we don't have one.
2603 if [[ -z $HG_PARENT ]]; then
2604 eval `$SED -e "s/#.*$//" $wxfile | $GREP HG_PARENT=`
2608 # If we still don't have a parent, we must have been given a
2609 # wx-style active list with no HG_PARENT specification, run
2610 # hg-active and pull an HG_PARENT out of it, ignore the rest.
2612 if [[ -z $HG_PARENT && -x $HG_ACTIVE ]]; then
2613 $HG_ACTIVE -w $codemgr_ws -p $real_parent | \
2614 eval `$SED -e "s/#.*$//" | $GREP HG_PARENT=`
2615 elif [[ -z $HG_PARENT ]]; then
2616 print
-u2 "Error: Cannot discover parent revision"
2619 elif [[ $SCM_MODE == "subversion" ]]; then
2622 # We only will have a real parent workspace in the case one
2623 # was specified (be it an older webrev, or another checkout).
2625 [[ -n $codemgr_parent ]] && PWS
=$codemgr_parent
2627 if [[ -z $flist_done && $flist_mode == "auto" ]]; then
2628 flist_from_subversion
$CWS $OLDPWD
2631 if [[ $SCM_MODE == "unknown" ]]; then
2632 print
-u2 " Unknown type of SCM in use"
2634 print
-u2 " Unsupported SCM in use: $SCM_MODE"
2639 if [[ -z $CODEMGR_WS ]]; then
2640 print
-u2 "SCM not detected/supported and CODEMGR_WS not specified"
2644 if [[ -z $CODEMGR_PARENT ]]; then
2645 print
-u2 "SCM not detected/supported and CODEMGR_PARENT not specified"
2654 # If the user didn't specify a -i option, check to see if there is a
2655 # webrev-info file in the workspace directory.
2657 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2659 INCLUDE_FILE
="$CWS/webrev-info"
2662 if [[ -n $iflag ]]; then
2663 if [[ ! -r $INCLUDE_FILE ]]; then
2664 print
-u2 "include file '$INCLUDE_FILE' does not exist or is" \
2669 # $INCLUDE_FILE may be a relative path, and the script alters
2670 # PWD, so we just stash a copy in /tmp.
2672 cp $INCLUDE_FILE /tmp
/$$.include
2676 # DO_EVERYTHING: break point
2677 if [[ -n $Nflag ]]; then
2682 typeset
-r its_sed_script
=/tmp
/$$.its_sed
2684 if [[ -z $nflag ]]; then
2685 DEFREGFILE
="$(dirname $(whence $0))/../etc/its.reg"
2686 if [[ -n $Iflag ]]; then
2688 elif [[ -r $HOME/.its.reg
]]; then
2689 REGFILE
=$HOME/.its.reg
2693 if [[ ! -r $REGFILE ]]; then
2694 print
"ERROR: Unable to read database registry file $REGFILE"
2696 elif [[ $REGFILE != $DEFREGFILE ]]; then
2697 print
" its.reg from: $REGFILE"
2700 $SED -e '/^#/d' -e '/^[ ]*$/d' $REGFILE |
while read LINE
; do
2705 if [[ $name == PREFIX
]]; then
2707 valid_prefixes
="${p} ${valid_prefixes}"
2709 itsinfo
["${p}_${name}"]="${value}"
2714 DEFCONFFILE
="$(dirname $(whence $0))/../etc/its.conf"
2715 CONFFILES
=$DEFCONFFILE
2716 if [[ -r $HOME/.its.conf
]]; then
2717 CONFFILES
="${CONFFILES} $HOME/.its.conf"
2719 if [[ -n $Cflag ]]; then
2720 CONFFILES
="${CONFFILES} ${ITSCONF}"
2724 for cf
in ${CONFFILES}; do
2725 if [[ ! -r $cf ]]; then
2726 print
"ERROR: Unable to read database configuration file $cf"
2728 elif [[ $cf != $DEFCONFFILE ]]; then
2729 print
" its.conf: reading $cf"
2731 $SED -e '/^#/d' -e '/^[ ]*$/d' $cf |
while read LINE
; do
2737 # If an information tracking system is explicitly identified by prefix,
2738 # we want to disregard the specified priorities and resolve it accordingly.
2740 # To that end, we'll build a sed script to do each valid prefix in turn.
2742 for p
in ${valid_prefixes}; do
2744 # When an informational URL was provided, translate it to a
2745 # hyperlink. When omitted, simply use the prefix text.
2747 if [[ -z ${itsinfo["${p}_INFO"]} ]]; then
2748 itsinfo
["${p}_INFO"]=${p}
2750 itsinfo
["${p}_INFO"]="<a href=\\\"${itsinfo["${p}_INFO"]}\\\">${p}</a>"
2754 # Assume that, for this invocation of webrev, all references
2755 # to this information tracking system should resolve through
2758 # If the caller specified -O, then always use EXTERNAL_URL.
2760 # Otherwise, look in the list of domains for a matching
2763 [[ -z $Oflag ]] && for d
in ${its_domain}; do
2764 if [[ -n ${itsinfo["${p}_INTERNAL_URL_${d}"]} ]]; then
2765 itsinfo
["${p}_URL"]="${itsinfo[${p}_INTERNAL_URL_${d}]}"
2769 if [[ -z ${itsinfo["${p}_URL"]} ]]; then
2770 itsinfo
["${p}_URL"]="${itsinfo[${p}_EXTERNAL_URL]}"
2774 # Turn the destination URL into a hyperlink
2776 itsinfo
["${p}_URL"]="<a href=\\\"${itsinfo[${p}_URL]}\\\">&</a>"
2778 # The character class below contains a literal tab
2779 print
"/^${p}[: ]/ {
2780 s;${itsinfo[${p}_REGEX]};${itsinfo[${p}_URL]};g
2781 s;^${p};${itsinfo[${p}_INFO]};
2782 }" >> ${its_sed_script}
2786 # The previous loop took care of explicit specification. Now use
2787 # the configured priorities to attempt implicit translations.
2789 for p
in ${its_priority}; do
2790 print
"/^${itsinfo[${p}_REGEX]}[ ]/ {
2791 s;^${itsinfo[${p}_REGEX]};${itsinfo[${p}_URL]};g
2792 }" >> ${its_sed_script}
2797 # Search for DO_EVERYTHING above for matching "for" statement
2798 # and explanation of this terminator.
2805 WDIR
=${WDIR:-$CWS/webrev}
2808 # Name of the webrev, derived from the workspace name or output directory;
2809 # in the future this could potentially be an option.
2811 if [[ -n $oflag ]]; then
2817 # Make sure remote target is well formed for remote upload/delete.
2818 if [[ -n $Dflag ||
-n $Uflag ]]; then
2820 # If remote target is not specified, build it from scratch using
2821 # the default values.
2823 if [[ -z $tflag ]]; then
2824 remote_target
=${DEFAULT_REMOTE_HOST}:${WNAME}
2827 # Check upload target prefix first.
2829 if [[ "${remote_target}" != ${rsync_prefix}* &&
2830 "${remote_target}" != ${ssh_prefix}* ]]; then
2831 print
"ERROR: invalid prefix of upload URI" \
2836 # If destination specification is not in the form of
2837 # host_spec:remote_dir then assume it is just remote hostname
2838 # and append a colon and destination directory formed from
2839 # local webrev directory name.
2841 typeset target_no_prefix
=${remote_target##*://}
2842 if [[ ${target_no_prefix} == *:* ]]; then
2843 if [[ "${remote_target}" == *: ]]; then
2844 remote_target
=${remote_target}${WNAME}
2847 if [[ ${target_no_prefix} == */* ]]; then
2848 print
"ERROR: badly formed upload URI" \
2852 remote_target
=${remote_target}:${WNAME}
2858 # Strip trailing slash. Each upload method will deal with directory
2859 # specification separately.
2861 remote_target
=${remote_target%/}
2865 # Option -D by itself (option -U not present) implies no webrev generation.
2867 if [[ -z $Uflag && -n $Dflag ]]; then
2873 # Do not generate the webrev, just upload it or delete it.
2875 if [[ -n $nflag ]]; then
2876 if [[ -n $Dflag ]]; then
2878 (( $?
== 0 )) ||
exit $?
2880 if [[ -n $Uflag ]]; then
2886 if [ "${WDIR%%/*}" ]; then
2890 if [[ ! -d $WDIR ]]; then
2892 (( $?
!= 0 )) && exit 1
2896 # Summarize what we're going to do.
2898 if [[ -n $CWS_REV ]]; then
2899 print
" Workspace: $CWS (at $CWS_REV)"
2901 print
" Workspace: $CWS"
2903 if [[ -n $parent_webrev ]]; then
2904 print
"Compare against: webrev at $parent_webrev"
2906 if [[ -n $HG_PARENT ]]; then
2907 hg_parent_short
=`echo $HG_PARENT \
2908 | $SED -e 's/\([0-9a-f]\{12\}\).*/\1/'`
2909 print
"Compare against: $PWS (at $hg_parent_short)"
2911 print
"Compare against: $PWS"
2915 [[ -n $INCLUDE_FILE ]] && print
" Including: $INCLUDE_FILE"
2916 print
" Output to: $WDIR"
2919 # Save the file list in the webrev dir
2921 [[ ! $FLIST -ef $WDIR/file.list
]] && cp $FLIST $WDIR/file.list
2923 rm -f $WDIR/$WNAME.
patch
2924 rm -f $WDIR/$WNAME.ps
2925 rm -f $WDIR/$WNAME.pdf
2927 touch $WDIR/$WNAME.
patch
2929 print
" Output Files:"
2932 # Clean up the file list: Remove comments, blank lines and env variables.
2934 $SED -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp
/$$.flist.clean
2935 FLIST
=/tmp
/$$.flist.clean
2938 # For Mercurial, create a cache of manifest entries.
2940 if [[ $SCM_MODE == "mercurial" ]]; then
2942 # Transform the FLIST into a temporary sed script that matches
2943 # relevant entries in the Mercurial manifest as follows:
2944 # 1) The script will be used against the parent revision manifest,
2945 # so for FLIST lines that have two filenames (a renamed file)
2946 # keep only the old name.
2947 # 2) Escape all forward slashes the filename.
2948 # 3) Change the filename into another sed command that matches
2949 # that file in "hg manifest -v" output: start of line, three
2950 # octal digits for file permissions, space, a file type flag
2951 # character, space, the filename, end of line.
2952 # 4) Eliminate any duplicate entries. (This can occur if a
2953 # file has been used as the source of an hg cp and it's
2954 # also been modified in the same changeset.)
2956 SEDFILE
=/tmp
/$$.manifest.
sed
2960 s#^.*$#/^... . &$/p#
2961 ' < $FLIST |
$SORT -u > $SEDFILE
2964 # Apply the generated script to the output of "hg manifest -v"
2965 # to get the relevant subset for this webrev.
2967 HG_PARENT_MANIFEST
=/tmp
/$$.manifest
2968 hg
-R $CWS manifest
-v -r $HG_PARENT |
2969 $SED -n -f $SEDFILE > $HG_PARENT_MANIFEST
2973 # First pass through the files: generate the per-file webrev HTML-files.
2975 cat $FLIST |
while read LINE
2981 # Normally, each line in the file list is just a pathname of a
2982 # file that has been modified or created in the child. A file
2983 # that is renamed in the child workspace has two names on the
2984 # line: new name followed by the old name.
2989 if [[ $# -eq 2 ]]; then
2990 PP
=$2 # old filename
2991 if [[ -f $PP ]]; then
2992 oldname
=" (copied from $PP)"
2994 oldname
=" (renamed from $PP)"
2999 if [[ $PDIR == $PP ]]; then
3000 PDIR
="." # File at root of workspace
3006 if [[ $DIR == $P ]]; then
3007 DIR
="." # File at root of workspace
3014 if [[ "$DIR" == "$P" ]]; then
3015 DIR
="." # File at root of workspace
3025 COMM
=`getcomments html $P $PP`
3027 print
"\t$P$oldname\n\t\t\c"
3029 # Make the webrev mirror directory if necessary
3033 # We stash old and new files into parallel directories in $WDIR
3034 # and do our diffs there. This makes it possible to generate
3035 # clean looking diffs which don't have absolute paths present.
3038 build_old_new
"$WDIR" "$PWS" "$PDIR" "$PF" "$CWS" "$DIR" "$F" || \
3042 # Keep the old PWD around, so we can safely switch back after
3043 # diff generation, such that build_old_new runs in a
3044 # consistent environment.
3052 cmp $ofile $nfile > /dev
/null
2>&1
3053 if [[ $?
== 0 && $rename == 1 ]]; then
3058 # If we have old and new versions of the file then run the appropriate
3059 # diffs. This is complicated by a couple of factors:
3061 # - renames must be handled specially: we emit a 'remove'
3062 # diff and an 'add' diff
3063 # - new files and deleted files must be handled specially
3064 # - Solaris patch(1m) can't cope with file creation
3065 # (and hence renames) as of this writing.
3066 # - To make matters worse, gnu patch doesn't interpret the
3067 # output of Solaris diff properly when it comes to
3068 # adds and deletes. We need to do some "cleansing"
3070 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@
3071 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@
3073 cleanse_rmfile
="$SED 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
3074 cleanse_newfile
="$SED 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
3076 rm -f $WDIR/$DIR/$F.
patch
3077 if [[ -z $rename ]]; then
3078 if [ ! -f "$ofile" ]; then
3079 diff -u /dev
/null
$nfile | sh
-c "$cleanse_newfile" \
3080 > $WDIR/$DIR/$F.
patch
3081 elif [ ! -f "$nfile" ]; then
3082 diff -u $ofile /dev
/null | sh
-c "$cleanse_rmfile" \
3083 > $WDIR/$DIR/$F.
patch
3085 diff -u $ofile $nfile > $WDIR/$DIR/$F.
patch
3088 diff -u $ofile /dev
/null | sh
-c "$cleanse_rmfile" \
3089 > $WDIR/$DIR/$F.
patch
3091 diff -u /dev
/null
$nfile | sh
-c "$cleanse_newfile" \
3092 >> $WDIR/$DIR/$F.
patch
3096 # Tack the patch we just made onto the accumulated patch for the
3099 cat $WDIR/$DIR/$F.
patch >> $WDIR/$WNAME.
patch
3103 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
3105 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
3106 diff_to_html
$F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
3107 > $WDIR/$DIR/$F.cdiff.html
3110 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
3111 diff_to_html
$F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
3112 > $WDIR/$DIR/$F.udiff.html
3116 if [[ -x $WDIFF ]]; then
3118 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
3119 $WDIR/$DIR/$F.wdiff.html
2>/dev
/null
3120 if [[ $?
-eq 0 ]]; then
3123 print
" wdiffs[fail]\c"
3127 sdiff_to_html
$ofile $nfile $F $DIR "$COMM" \
3128 > $WDIR/$DIR/$F.
sdiff.html
3133 rm -f $WDIR/$DIR/$F.cdiff
$WDIR/$DIR/$F.udiff
3135 difflines
$ofile $nfile > $WDIR/$DIR/$F.count
3137 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
3138 # renamed file: may also have differences
3139 difflines
$ofile $nfile > $WDIR/$DIR/$F.count
3140 elif [[ -f $nfile ]]; then
3141 # new file: count added lines
3142 difflines
/dev
/null
$nfile > $WDIR/$DIR/$F.count
3143 elif [[ -f $ofile ]]; then
3144 # old file: count deleted lines
3145 difflines
$ofile /dev
/null
> $WDIR/$DIR/$F.count
3149 # Now we generate the postscript for this file. We generate diffs
3150 # only in the event that there is delta, or the file is new (it seems
3151 # tree-killing to print out the contents of deleted files).
3153 if [[ -f $nfile ]]; then
3155 [[ ! -f $ofile ]] && ocr
=/dev
/null
3157 if [[ -z $mv_but_nodiff ]]; then
3158 textcomm
=`getcomments text $P $PP`
3159 if [[ -x $CODEREVIEW ]]; then
3160 $CODEREVIEW -y "$textcomm" \
3162 > /tmp
/$$.psfile
2>/dev
/null
&&
3163 cat /tmp
/$$.psfile
>> $WDIR/$WNAME.ps
3164 if [[ $?
-eq 0 ]]; then
3173 if [[ -f $ofile ]]; then
3174 source_to_html Old
$PP < $ofile > $WDIR/$DIR/$F-.html
3178 if [[ -f $nfile ]]; then
3179 source_to_html New
$P < $nfile > $WDIR/$DIR/$F.html
3188 frame_nav_js
> $WDIR/ancnav.js
3189 frame_navigation
> $WDIR/ancnav.html
3191 if [[ ! -f $WDIR/$WNAME.ps
]]; then
3192 print
" Generating PDF: Skipped: no output available"
3193 elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then
3194 print
" Generating PDF: \c"
3195 fix_postscript
$WDIR/$WNAME.ps |
$PS2PDF - > $WDIR/$WNAME.pdf
3198 print
" Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'"
3201 # If we're in OpenSolaris mode and there's a closed dir under $WDIR,
3202 # delete it - prevent accidental publishing of closed source
3204 if [[ -n "$Oflag" ]]; then
3205 $FIND $WDIR -type d
-name closed
-exec /bin
/rm -rf {} \
;
3208 # Now build the index.html file that contains
3209 # links to the source files and their diffs.
3213 # Save total changed lines for Code Inspection.
3214 print
"$TOTL" > $WDIR/TotalChangedLines
3216 print
" index.html: \c"
3217 INDEXFILE
=$WDIR/index.html
3218 exec 3<&1 # duplicate stdout to FD3.
3219 exec 1<&- # Close stdout.
3220 exec > $INDEXFILE # Open stdout to index file.
3222 print
"$HTML<head>$STDHEAD"
3223 print
"<title>$WNAME</title>"
3225 print
"<body id=\"SUNWwebrev\">"
3226 print
"<div class=\"summary\">"
3227 print
"<h2>Code Review for $WNAME</h2>"
3232 # Get the preparer's name:
3234 # If the SCM detected is Mercurial, and the configuration property
3235 # ui.username is available, use that, but be careful to properly escape
3236 # angle brackets (HTML syntax characters) in the email address.
3238 # Otherwise, use the current userid in the form "John Doe (jdoe)", but
3239 # to maintain compatibility with passwd(4), we must support '&' substitutions.
3242 if [[ "$SCM_MODE" == mercurial
]]; then
3243 preparer
=`hg showconfig ui.username 2>/dev/null`
3244 if [[ -n "$preparer" ]]; then
3245 preparer
="$(echo "$preparer" | html_quote)"
3248 if [[ -z "$preparer" ]]; then
3251 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<);
3253 $gcos =~ s/\&/ucfirst($login)/e;
3254 printf "%s (%s)\n", $gcos, $login;
3256 printf "(unknown)\n";
3261 PREPDATE
=$
(LC_ALL
=C
/usr
/bin
/date +%Y-
%b-
%d\
%R\
%z\
%Z
)
3262 print
"<tr><th>Prepared by:</th><td>$preparer on $PREPDATE</td></tr>"
3263 print
"<tr><th>Workspace:</th><td>$CWS"
3264 if [[ -n $CWS_REV ]]; then
3265 print
"(at $CWS_REV)"
3268 print
"<tr><th>Compare against:</th><td>"
3269 if [[ -n $parent_webrev ]]; then
3270 print
"webrev at $parent_webrev"
3273 if [[ -n $hg_parent_short ]]; then
3274 print
"(at $hg_parent_short)"
3278 print
"<tr><th>Summary of changes:</th><td>"
3279 printCI
$TOTL $TINS $TDEL $TMOD $TUNC
3282 if [[ -f $WDIR/$WNAME.
patch ]]; then
3283 wpatch_url
="$(print $WNAME.patch | url_encode)"
3284 print
"<tr><th>Patch of changes:</th><td>"
3285 print
"<a href=\"$wpatch_url\">$WNAME.patch</a></td></tr>"
3287 if [[ -f $WDIR/$WNAME.pdf
]]; then
3288 wpdf_url
="$(print $WNAME.pdf | url_encode)"
3289 print
"<tr><th>Printable review:</th><td>"
3290 print
"<a href=\"$wpdf_url\">$WNAME.pdf</a></td></tr>"
3293 if [[ -n "$iflag" ]]; then
3294 print
"<tr><th>Author comments:</th><td><div>"
3296 print
"</div></td></tr>"
3302 # Second pass through the files: generate the rest of the index file
3304 cat $FLIST |
while read LINE
3309 if [[ $# == 2 ]]; then
3318 cmp $WDIR/raw_files
/old
/$PP $WDIR/raw_files
/new
/$P > /dev
/null
2>&1
3319 if [[ $?
== 0 && -n "$oldname" ]]; then
3324 if [[ $DIR == $P ]]; then
3325 DIR
="." # File at root of workspace
3328 # Avoid processing the same file twice.
3329 # It's possible for renamed files to
3330 # appear twice in the file list
3336 # If there's a diffs file, make diffs links
3338 if [[ -f $F.cdiff.html
]]; then
3339 cdiff_url
="$(print $P.cdiff.html | url_encode)"
3340 udiff_url
="$(print $P.udiff.html | url_encode)"
3341 print
"<a href=\"$cdiff_url\">Cdiffs</a>"
3342 print
"<a href=\"$udiff_url\">Udiffs</a>"
3344 if [[ -f $F.wdiff.html
&& -x $WDIFF ]]; then
3345 wdiff_url
="$(print $P.wdiff.html | url_encode)"
3346 print
"<a href=\"$wdiff_url\">Wdiffs</a>"
3349 sdiff_url
="$(print $P.sdiff.html | url_encode)"
3350 print
"<a href=\"$sdiff_url\">Sdiffs</a>"
3352 frames_url
="$(print $P.frames.html | url_encode)"
3353 print
"<a href=\"$frames_url\">Frames</a>"
3355 print
" ------ ------ ------"
3357 if [[ -x $WDIFF ]]; then
3364 # If there's an old file, make the link
3366 if [[ -f $F-.html
]]; then
3367 oldfile_url
="$(print $P-.html | url_encode)"
3368 print
"<a href=\"$oldfile_url\">Old</a>"
3373 # If there's an new file, make the link
3375 if [[ -f $F.html
]]; then
3376 newfile_url
="$(print $P.html | url_encode)"
3377 print
"<a href=\"$newfile_url\">New</a>"
3382 if [[ -f $F.
patch ]]; then
3383 patch_url
="$(print $P.patch | url_encode)"
3384 print
"<a href=\"$patch_url\">Patch</a>"
3389 if [[ -f $WDIR/raw_files
/new
/$P ]]; then
3390 rawfiles_url
="$(print raw_files/new/$P | url_encode)"
3391 print
"<a href=\"$rawfiles_url\">Raw</a>"
3398 # For renamed files, clearly state whether or not they are modified
3399 if [[ -f "$oldname" ]]; then
3400 if [[ -n "$mv_but_nodiff" ]]; then
3401 print
"<i>(copied from $oldname)</i>"
3403 print
"<i>(copied and modified from $oldname)</i>"
3405 elif [[ -n "$oldname" ]]; then
3406 if [[ -n "$mv_but_nodiff" ]]; then
3407 print
"<i>(renamed from $oldname)</i>"
3409 print
"<i>(renamed and modified from $oldname)</i>"
3413 # If there's an old file, but no new file, the file was deleted
3414 if [[ -f $F-.html
&& ! -f $F.html
]]; then
3415 print
" <i>(deleted)</i>"
3419 # Check for usr/closed and deleted_files/usr/closed
3421 if [ ! -z "$Oflag" ]; then
3422 if [[ $P == usr
/closed
/* || \
3423 $P == deleted_files
/usr
/closed
/* ]]; then
3424 print
" <i>Closed source: omitted from" \
3430 # Insert delta comments
3432 print
"<blockquote><pre>"
3433 getcomments html
$P $PP
3436 # Add additional comments comment
3438 print
"<!-- Add comments to explain changes in $P here -->"
3440 # Add count of changes.
3442 if [[ -f $F.count
]]; then
3447 if [[ $SCM_MODE == "teamware" ||
3448 $SCM_MODE == "mercurial" ||
3449 $SCM_MODE == "unknown" ]]; then
3451 # Include warnings for important file mode situations:
3452 # 1) New executable files
3453 # 2) Permission changes of any kind
3454 # 3) Existing executable files
3457 if [[ -f $WDIR/raw_files
/old
/$PP ]]; then
3458 old_mode
=`get_file_mode $WDIR/raw_files/old/$PP`
3462 if [[ -f $WDIR/raw_files
/new
/$P ]]; then
3463 new_mode
=`get_file_mode $WDIR/raw_files/new/$P`
3466 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3467 print
"<span class=\"chmod\">"
3468 print
"<p>new executable file: mode $new_mode</p>"
3470 elif [[ -n "$old_mode" && -n "$new_mode" &&
3471 "$old_mode" != "$new_mode" ]]; then
3472 print
"<span class=\"chmod\">"
3473 print
"<p>mode change: $old_mode to $new_mode</p>"
3475 elif [[ "$new_mode" = *[1357]* ]]; then
3476 print
"<span class=\"chmod\">"
3477 print
"<p>executable file: mode $new_mode</p>"
3482 print
"</blockquote>"
3488 print
"<p style=\"font-size: small\">"
3489 print
"This code review page was prepared using <b>$0</b>."
3490 print
"Webrev is maintained by the <a href=\"http://www.opensolaris.org\">"
3491 print
"OpenSolaris</a> project. The latest version may be obtained"
3492 print
"<a href=\"http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>"
3496 exec 1<&- # Close FD 1.
3497 exec 1<&3 # dup FD 3 to restore stdout.
3498 exec 3<&- # close FD 3.
3503 # If remote deletion was specified and fails do not continue.
3505 if [[ -n $Dflag ]]; then
3507 (( $?
== 0 )) ||
exit $?
3510 if [[ -n $Uflag ]]; then