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.
25 # Copyright 2008, 2010, Richard Lowe
26 # Copyright 2012 Marcel Telka <marcel@telka.sk>
27 # Copyright 2014 Bart Coddens <bart.coddens@gmail.com>
28 # Copyright 2017 Nexenta Systems, Inc.
29 # Copyright 2016 Joyent, Inc.
30 # Copyright 2016 RackTop Systems.
34 # This script takes a file list and a workspace and builds a set of html files
35 # suitable for doing a code review of source changes via a web page.
36 # Documentation is available via the manual page, webrev.1, or just
39 # Acknowledgements to contributors to webrev are listed in the webrev(1)
47 HTML
='<?xml version="1.0"?>
48 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
49 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
52 FRAMEHTML
='<?xml version="1.0"?>
53 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
54 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
55 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
57 STDHEAD
='<meta http-equiv="cache-control" content="no-cache"></meta>
58 <meta http-equiv="Content-Type" content="text/xhtml;charset=utf-8"></meta>
59 <meta http-equiv="Pragma" content="no-cache"></meta>
60 <meta http-equiv="Expires" content="-1"></meta>
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.
66 For example, to have all "removed" information be red instead of
67 brown, set a rule in your userContent.css file like:
69 body#SUNWwebrev span.removed { color: red ! important; }
71 <style type="text/css" media="screen">
73 background-color: #eeeeee;
77 border-top: 1px solid #aaa;
82 border-bottom: 1px solid #aaa;
89 div.summary table th {
121 a.print { font-size: x-small; }
122 a:hover { background-color: #ffcc99; }
125 <style type="text/css" media="print">
126 pre { font-size: 0.8em; font-family: courier, monospace; }
127 span.removed { color: #444; font-style: italic }
128 span.changed { font-weight: bold; }
129 span.new { font-weight: bold; }
130 span.newmarker { font-size: 1.2em; font-weight: bold; }
131 span.oldmarker { font-size: 1.2em; font-weight: bold; }
132 a.print {display: none}
133 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
138 # UDiffs need a slightly different CSS rule for 'new' items (we don't
139 # want them to be bolded as we do in cdiffs or sdiffs).
142 <style type="text/css" media="screen">
150 # CSS for the HTML version of the man pages.
151 # Current version is from mandoc 1.14.4.
153 /* $Id: mandoc.css,v 1.36 2018/07/23 22:51:26 schwarze Exp $ */
155 * Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
158 /* Global defaults. */
160 html { max-width: 65em; }
161 body { font-family: Helvetica,Arial,sans-serif; }
162 table { margin-top: 0em;
163 margin-bottom: 0em; }
164 td { vertical-align: top; }
165 ul, ol, dl { margin-top: 0em;
166 margin-bottom: 0em; }
167 li, dt { margin-top: 1em; }
169 .permalink { border-bottom: thin dotted;
172 text-decoration: inherit; }
175 /* Search form and search results. */
177 fieldset { border: thin solid silver;
179 text-align: center; }
183 table.results { margin-top: 1em;
185 font-size: smaller; }
187 /* Header and footer lines. */
189 table.head { width: 100%;
190 border-bottom: 1px dotted #808080;
192 font-size: smaller; }
193 td.head-vol { text-align: center; }
197 table.foot { width: 100%;
198 border-top: 1px dotted #808080;
200 font-size: smaller; }
201 td.foot-os { text-align: right; }
203 /* Sections and paragraphs. */
206 margin-left: 3.8em; }
207 .Nd { display: inline; }
208 .Sh { margin-top: 1.2em;
209 margin-bottom: 0.6em;
212 .Ss { margin-top: 1.2em;
213 margin-bottom: 0.6em;
216 .Pp { margin: 0.6em 0em; }
220 /* Displays and lists. */
223 .Bd-indent { margin-left: 3.8em; }
225 .Bl-bullet { list-style-type: disc;
228 .Bl-dash { list-style-type: none;
230 .Bl-dash > li:before {
232 .Bl-item { list-style-type: none;
238 .Bl-enum { padding-left: 2em; }
252 margin-left: 5.5em; }
261 .Bl-tag { margin-left: 5.5em; }
266 padding-right: 1.2em;
267 vertical-align: top; }
279 .Bl-column > tbody > tr { }
280 .Bl-column > tbody > tr > td {
282 .Bl-compact > tbody > tr > td {
285 .Rs { font-style: normal;
286 font-weight: normal; }
288 .RsB { font-style: italic;
289 font-weight: normal; }
292 .RsI { font-style: italic;
293 font-weight: normal; }
294 .RsJ { font-style: italic;
295 font-weight: normal; }
301 .RsT { text-decoration: underline; }
308 .HP { margin-left: 3.8em;
309 text-indent: -3.8em; }
311 /* Semantic markup for command line utilities. */
314 code.Nm { font-style: normal;
316 font-family: inherit; }
317 .Fl { font-style: normal;
319 font-family: inherit; }
320 .Cm { font-style: normal;
322 font-family: inherit; }
323 .Ar { font-style: italic;
324 font-weight: normal; }
325 .Op { display: inline; }
326 .Ic { font-style: normal;
328 font-family: inherit; }
329 .Ev { font-style: normal;
331 font-family: monospace; }
332 .Pa { font-style: italic;
333 font-weight: normal; }
335 /* Semantic markup for function libraries. */
338 code.In { font-style: normal;
340 font-family: inherit; }
342 .Fd { font-style: normal;
344 font-family: inherit; }
345 .Ft { font-style: italic;
346 font-weight: normal; }
347 .Fn { font-style: normal;
349 font-family: inherit; }
350 .Fa { font-style: italic;
351 font-weight: normal; }
352 .Vt { font-style: italic;
353 font-weight: normal; }
354 .Va { font-style: italic;
355 font-weight: normal; }
356 .Dv { font-style: normal;
358 font-family: monospace; }
359 .Er { font-style: normal;
361 font-family: monospace; }
363 /* Various semantic markup. */
368 .Cd { font-style: normal;
370 font-family: inherit; }
371 .Ad { font-style: italic;
372 font-weight: normal; }
373 .Ms { font-style: normal;
378 /* Physical markup. */
380 .Bf { display: inline; }
381 .No { font-style: normal;
382 font-weight: normal; }
383 .Em { font-style: italic;
384 font-weight: normal; }
385 .Sy { font-style: normal;
387 .Li { font-style: normal;
389 font-family: monospace; }
391 /* Overrides to avoid excessive margins on small devices. */
393 @media (max-width: 37.5em) {
395 margin-left: 0.5em; }
396 .Sh, .Ss { margin-left: 0em; }
397 .Bd-indent { margin-left: 2em; }
400 .Bl-tag { margin-left: 2em; }
403 .HP { margin-left: 2em;
409 # Display remote target with prefix and trailing slash.
411 function print_upload_header
414 typeset display_target
416 if [[ -z $tflag ]]; then
417 display_target
=${prefix}${remote_target}
419 display_target
=${remote_target}
422 if [[ ${display_target} != */ ]]; then
423 display_target
=${display_target}/
426 print
" Upload to: ${display_target}\n" \
431 # Upload the webrev via rsync. Return 0 on success, 1 on error.
433 function rsync_upload
435 if (( $# != 2 )); then
436 print
"\nERROR: rsync_upload: wrong usage ($#)"
441 integer
-r print_err_msg
=$2
443 print_upload_header
${rsync_prefix}
445 typeset
-r err_msg
=$
( $MKTEMP /tmp
/rsync_err.XXXXXX
)
446 if [[ -z $err_msg ]]; then
447 print
"\nERROR: rsync_upload: cannot create temporary file"
451 # The source directory must end with a slash in order to copy just
452 # directory contents, not the whole directory.
454 typeset src_dir
=$WDIR
455 if [[ ${src_dir} != */ ]]; then
458 $RSYNC -r -q ${src_dir} $dst 2>$err_msg
459 if (( $?
!= 0 )); then
460 if (( ${print_err_msg} > 0 )); then
461 print
"Failed.\nERROR: rsync failed"
462 print
"src dir: '${src_dir}'\ndst dir: '$dst'"
463 print
"error messages:"
464 $SED 's/^/> /' $err_msg
476 # Create directories on remote host using SFTP. Return 0 on success,
479 function remote_mkdirs
481 typeset
-r dir_spec
=$1
482 typeset
-r host_spec
=$2
485 # If the supplied path is absolute we assume all directories are
486 # created, otherwise try to create all directories in the path
487 # except the last one which will be created by scp.
489 if [[ "${dir_spec}" == */* && "${dir_spec}" != /* ]]; then
492 # Remove the last directory from directory specification.
494 typeset
-r dirs_mk
=${dir_spec%/*}
495 typeset
-r batch_file_mkdir
=$
( $MKTEMP \
496 /tmp
/webrev_mkdir.XXXXXX
)
497 if [[ -z $batch_file_mkdir ]]; then
498 print
"\nERROR: remote_mkdirs:" \
499 "cannot create temporary file for batch file"
505 for dir
in ${dirs_mk}; do
507 # Use the '-' prefix to ignore mkdir errors in order
508 # to avoid an error in case the directory already
509 # exists. We check the directory with chdir to be sure
512 print
-- "-mkdir ${dir}" >> ${batch_file_mkdir}
513 print
"chdir ${dir}" >> ${batch_file_mkdir}
516 typeset
-r sftp_err_msg
=$
( $MKTEMP /tmp
/webrev_scp_err.XXXXXX
)
517 if [[ -z ${sftp_err_msg} ]]; then
518 print
"\nERROR: remote_mkdirs:" \
519 "cannot create temporary file for error messages"
522 $SFTP -b ${batch_file_mkdir} ${host_spec} 2>${sftp_err_msg} 1>&2
523 if (( $?
!= 0 )); then
524 print
"\nERROR: failed to create remote directories"
525 print
"error messages:"
526 $SED 's/^/> /' ${sftp_err_msg}
527 rm -f ${sftp_err_msg} ${batch_file_mkdir}
530 rm -f ${sftp_err_msg} ${batch_file_mkdir}
537 # Upload the webrev via SSH. Return 0 on success, 1 on error.
541 if (( $# != 1 )); then
542 print
"\nERROR: ssh_upload: wrong number of arguments"
547 typeset
-r host_spec
=${dst%%:*}
548 typeset
-r dir_spec
=${dst#*:}
551 # Display the upload information before calling delete_webrev
552 # because it will also print its progress.
554 print_upload_header
${ssh_prefix}
557 # If the deletion was explicitly requested there is no need
558 # to perform it again.
560 if [[ -z $Dflag ]]; then
562 # We do not care about return value because this might be
563 # the first time this directory is uploaded.
569 # Create remote directories. Any error reporting will be done
570 # in remote_mkdirs function.
572 remote_mkdirs
${dir_spec} ${host_spec}
573 if (( $?
!= 0 )); then
577 print
"upload ... \c"
578 typeset
-r scp_err_msg
=$
( $MKTEMP /tmp
/scp_err.XXXXXX
)
579 if [[ -z ${scp_err_msg} ]]; then
580 print
"\nERROR: ssh_upload:" \
581 "cannot create temporary file for error messages"
584 $SCP -q -C -B -o PreferredAuthentications
=publickey
-r \
585 $WDIR $dst 2>${scp_err_msg}
586 if (( $?
!= 0 )); then
587 print
"Failed.\nERROR: scp failed"
588 print
"src dir: '$WDIR'\ndst dir: '$dst'"
589 print
"error messages:"
590 $SED 's/^/> /' ${scp_err_msg}
601 # Delete webrev at remote site. Return 0 on success, 1 or exit code from sftp
602 # on failure. If first argument is 1 then perform the check of sftp return
603 # value otherwise ignore it. If second argument is present it means this run
604 # only performs deletion.
606 function delete_webrev
608 if (( $# < 1 )); then
609 print
"delete_webrev: wrong number of arguments"
614 integer delete_only
=0
615 if (( $# == 2 )); then
620 # Strip the transport specification part of remote target first.
622 typeset
-r stripped_target
=${remote_target##*://}
623 typeset
-r host_spec
=${stripped_target%%:*}
624 typeset
-r dir_spec
=${stripped_target#*:}
628 # Do not accept an absolute path.
630 if [[ ${dir_spec} == /* ]]; then
635 # Strip the ending slash.
637 if [[ ${dir_spec} == */ ]]; then
638 dir_rm
=${dir_spec%%/}
643 if (( ${delete_only} > 0 )); then
644 print
" Removing: \c"
648 if [[ -z "$dir_rm" ]]; then
649 print
"\nERROR: empty directory for removal"
654 # Prepare batch file.
656 typeset
-r batch_file_rm
=$
( $MKTEMP /tmp
/webrev_remove.XXXXXX
)
657 if [[ -z $batch_file_rm ]]; then
658 print
"\nERROR: delete_webrev: cannot create temporary file"
661 print
"rename $dir_rm $TRASH_DIR/removed.$$" > $batch_file_rm
664 # Perform remote deletion and remove the batch file.
666 typeset
-r sftp_err_msg
=$
( $MKTEMP /tmp
/webrev_scp_err.XXXXXX
)
667 if [[ -z ${sftp_err_msg} ]]; then
668 print
"\nERROR: delete_webrev:" \
669 "cannot create temporary file for error messages"
672 $SFTP -b $batch_file_rm $host_spec 2>${sftp_err_msg} 1>&2
675 if (( $ret != 0 && $check > 0 )); then
676 print
"Failed.\nERROR: failed to remove remote directories"
677 print
"error messages:"
678 $SED 's/^/> /' ${sftp_err_msg}
679 rm -f ${sftp_err_msg}
682 rm -f ${sftp_err_msg}
683 if (( ${delete_only} > 0 )); then
691 # Upload webrev to remote site
693 function upload_webrev
697 if [[ ! -d "$WDIR" ]]; then
698 print
"\nERROR: webrev directory '$WDIR' does not exist"
703 # Perform a late check to make sure we do not upload closed source
704 # to remote target when -n is used. If the user used custom remote
705 # target he probably knows what he is doing.
707 if [[ -n $nflag && -z $tflag ]]; then
708 $FIND $WDIR -type d
-name closed \
709 |
$GREP closed
>/dev
/null
710 if (( $?
== 0 )); then
711 print
"\nERROR: directory '$WDIR' contains" \
712 "\"closed\" directory"
719 # We have the URI for remote destination now so let's start the upload.
721 if [[ -n $tflag ]]; then
722 if [[ "${remote_target}" == ${rsync_prefix}?
* ]]; then
723 rsync_upload
${remote_target##$rsync_prefix} 1
726 elif [[ "${remote_target}" == ${ssh_prefix}?
* ]]; then
727 ssh_upload
${remote_target##$ssh_prefix}
733 # Try rsync first and fallback to SSH in case it fails.
735 rsync_upload
${remote_target} 0
737 if (( $ret != 0 )); then
738 print
"Failed. (falling back to SSH)"
739 ssh_upload
${remote_target}
747 # input_cmd | url_encode | output_cmd
749 # URL-encode (percent-encode) reserved characters as defined in RFC 3986.
751 # Reserved characters are: :/?#[]@!$&'()*+,;=
753 # While not a reserved character itself, percent '%' is reserved by definition
754 # so encode it first to avoid recursive transformation, and skip '/' which is
757 # The quotation character is deliberately not escaped in order to make
758 # the substitution work with GNU sed.
762 $SED -e "s|%|%25|g" -e "s|:|%3A|g" -e "s|\&|%26|g" \
763 -e "s|?|%3F|g" -e "s|#|%23|g" -e "s|\[|%5B|g" \
764 -e "s|*|%2A|g" -e "s|@|%40|g" -e "s|\!|%21|g" \
765 -e "s|=|%3D|g" -e "s|;|%3B|g" -e "s|\]|%5D|g" \
766 -e "s|(|%28|g" -e "s|)|%29|g" -e "s|'|%27|g" \
767 -e "s|+|%2B|g" -e "s|\,|%2C|g" -e "s|\\\$|%24|g"
771 # input_cmd | html_quote | output_cmd
773 # html_quote filename | output_cmd
775 # Make a piece of source code safe for display in an HTML <pre> block.
779 $SED -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" "$@" |
expand
783 # Trim a digest-style revision to a conventionally readable yet useful length
789 echo $digest |
$SED -e 's/\([0-9a-f]\{12\}\).*/\1/'
793 # input_cmd | its2url | output_cmd
795 # Scan for information tracking system references and insert <a> links to the
796 # relevant databases.
800 $SED -f ${its_sed_script}
804 # strip_unchanged <infile> | output_cmd
806 # Removes chunks of sdiff documents that have not changed. This makes it
807 # easier for a code reviewer to find the bits that have changed.
809 # Deleted lines of text are replaced by a horizontal rule. Some
810 # identical lines are retained before and after the changed lines to
811 # provide some context. The number of these lines is controlled by the
812 # variable C in the $AWK script below.
814 # The script detects changed lines as any line that has a "<span class="
815 # string embedded (unchanged lines have no particular class and are not
816 # part of a <span>). Blank lines (without a sequence number) are also
817 # detected since they flag lines that have been inserted or deleted.
823 NF == 0 || /<span class="/ {
828 print "\n</pre><hr></hr><pre>"
833 for (i = 0; i < c; i++)
834 print ln[(inx + i) % C]
848 END { if (c > (C * 2)) print "\n</pre><hr></hr>" }
856 # This function takes two files as arguments, obtains their diff, and
857 # processes the diff output to present the files as an HTML document with
858 # the files displayed side-by-side, differences shown in color. It also
859 # takes a delta comment, rendered as an HTML snippet, as the third
860 # argument. The function takes two files as arguments, then the name of
861 # file, the path, and the comment. The HTML will be delivered on stdout,
864 # $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
865 # new/usr/src/tools/scripts/webrev.sh \
866 # webrev.sh usr/src/tools/scripts \
867 # '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
868 # 1234567</a> my bugid' > <file>.html
870 # framed_sdiff() is then called which creates $2.frames.html
871 # in the webrev tree.
873 # FYI: This function is rather unusual in its use of awk. The initial
874 # diff run produces conventional diff output showing changed lines mixed
875 # with editing codes. The changed lines are ignored - we're interested in
876 # the editing codes, e.g.
885 # These editing codes are parsed by the awk script and used to generate
886 # another awk script that generates HTML, e.g the above lines would turn
887 # into something like this:
889 # BEGIN { printf "<pre>\n" }
890 # function sp(n) {for (i=0;i<n;i++)printf "\n"}
891 # function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
892 # NR==8 {wl("#7A7ADD");next}
893 # NR==54 {wl("#7A7ADD");sp(3);next}
894 # NR==56 {wl("#7A7ADD");next}
895 # NR==57 {wl("black");printf "\n"; next}
898 # This script is then run on the original source file to generate the
899 # HTML that corresponds to the source file.
901 # The two HTML files are then combined into a single piece of HTML that
902 # uses an HTML table construct to present the files side by side. You'll
903 # notice that the changes are color-coded:
905 # black - unchanged lines
906 # blue - changed lines
907 # bold blue - new lines
908 # brown - deleted lines
910 # Blank lines are inserted in each file to keep unchanged lines in sync
911 # (side-by-side). This format is familiar to users of sdiff(1) or
912 # Teamware's filemerge tool.
916 diff -b $1 $2 > /tmp
/$$.diffs
923 # Now we have the diffs, generate the HTML for the old file.
927 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
928 printf "function removed() "
929 printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
930 printf "function changed() "
931 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
932 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
939 split($1, a, /[cad]/) ;
940 if (index($1, "a")) {
942 n = split(a[2], r, /,/);
944 printf "BEGIN\t\t{sp(1)}\n"
946 printf "BEGIN\t\t{sp(%d)}\n",\
951 printf "NR==%s\t\t{", a[1]
952 n = split(a[2], r, /,/);
955 printf "bl();printf \"\\n\"; next}\n"
958 printf "bl();sp(%d);next}\n",\
963 if (index($1, "d")) {
964 n = split(a[1], r, /,/);
968 printf "NR==%s\t\t{removed(); next}\n" , n1
970 printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
973 if (index($1, "c")) {
974 n = split(a[1], r, /,/);
980 printf "NR==%s\t\t{changed();" , n1
983 printf "NR==%s,NR==%s\t{changed();" , n1, n2
985 m = split(a[2], r, /,/);
991 if (n > 1) printf "if (NR==%d)", final
992 printf "sp(%d);", d2 - d1
1001 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
1002 ' /tmp
/$$.diffs
> /tmp
/$$.file1
1005 # Now generate the HTML for the new file
1009 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
1010 printf "function new() "
1011 printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
1012 printf "function changed() "
1013 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
1014 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
1022 split($1, a, /[cad]/) ;
1023 if (index($1, "d")) {
1025 n = split(a[1], r, /,/);
1027 printf "BEGIN\t\t{sp(1)}\n"
1029 printf "BEGIN\t\t{sp(%d)}\n",\
1034 printf "NR==%s\t\t{", a[2]
1035 n = split(a[1], r, /,/);
1038 printf "bl();printf \"\\n\"; next}\n"
1041 printf "bl();sp(%d);next}\n",\
1046 if (index($1, "a")) {
1047 n = split(a[2], r, /,/);
1051 printf "NR==%s\t\t{new() ; next}\n" , n1
1053 printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
1056 if (index($1, "c")) {
1057 n = split(a[2], r, /,/);
1064 printf "NR==%s\t\t{changed();" , n1
1067 printf "NR==%s,NR==%s\t{changed();" , n1, n2
1069 m = split(a[1], r, /,/);
1075 if (n > 1) printf "if (NR==%d)", final
1076 printf "sp(%d);", d1 - d2
1083 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
1084 ' /tmp
/$$.diffs
> /tmp
/$$.file2
1087 # Post-process the HTML files by running them back through $AWK
1089 html_quote
< $1 |
$AWK -f /tmp
/$$.file1
> /tmp
/$$.file1.html
1091 html_quote
< $2 |
$AWK -f /tmp
/$$.file2
> /tmp
/$$.file2.html
1094 # Now combine into a valid HTML file and side-by-side into a table
1096 print
"$HTML<head>$STDHEAD"
1097 print
"<title>$WNAME Sdiff $TPATH/$TNAME</title>"
1098 print
"</head><body id=\"SUNWwebrev\">"
1099 print
"<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
1100 print
"<pre>$COMMENT</pre>\n"
1101 print
"<table><tr valign=\"top\">"
1104 strip_unchanged
/tmp
/$$.file1.html
1106 print
"</pre></td><td><pre>"
1108 strip_unchanged
/tmp
/$$.file2.html
1111 print
"</tr></table>"
1112 print
"</body></html>"
1114 framed_sdiff
$TNAME $TPATH /tmp
/$$.file1.html
/tmp
/$$.file2.html \
1120 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
1122 # Expects lefthand and righthand side html files created by sdiff_to_html.
1123 # We use insert_anchors() to augment those with HTML navigation anchors,
1124 # and then emit the main frame. Content is placed into:
1126 # $WDIR/DIR/$TNAME.lhs.html
1127 # $WDIR/DIR/$TNAME.rhs.html
1128 # $WDIR/DIR/$TNAME.frames.html
1130 # NOTE: We rely on standard usage of $WDIR and $DIR.
1132 function framed_sdiff
1141 # Enable html files to access WDIR via a relative path.
1142 RTOP
=$
(relative_dir
$TPATH $WDIR)
1144 # Make the rhs/lhs files and output the frameset file.
1145 print
"$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
1147 cat >> $WDIR/$DIR/$TNAME.lhs.html
<<-EOF
1148 <script type="text/javascript" src="${RTOP}ancnav.js"></script>
1150 <body id="SUNWwebrev" onkeypress="keypress(event);">
1152 <pre>$comments</pre><hr></hr>
1155 cp $WDIR/$DIR/$TNAME.lhs.html
$WDIR/$DIR/$TNAME.rhs.html
1157 insert_anchors
$lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
1158 insert_anchors
$rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
1160 close
='</body></html>'
1162 print
$close >> $WDIR/$DIR/$TNAME.lhs.html
1163 print
$close >> $WDIR/$DIR/$TNAME.rhs.html
1165 print
"$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
1166 print
"<title>$WNAME Framed-Sdiff " \
1167 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
1168 cat >> $WDIR/$DIR/$TNAME.frames.html
<<-EOF
1169 <frameset rows="*,60">
1170 <frameset cols="50%,50%">
1171 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs"></frame>
1172 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame>
1174 <frame src="${RTOP}ancnav.html" scrolling="no" marginwidth="0"
1175 marginheight="0" name="nav"></frame>
1177 <body id="SUNWwebrev">
1178 Alas 'frames' webrev requires that your browser supports frames
1179 and has the feature enabled.
1191 # Merge codereview output files to a single conforming postscript file, by:
1192 # - removing all extraneous headers/trailers
1193 # - making the page numbers right
1194 # - removing pages devoid of contents which confuse some
1195 # postscript readers.
1199 function fix_postscript
1203 cat > /tmp
/$$.crmerge.pl
<< \EOF
1205 print scalar
(<>); # %!PS-Adobe---
1206 print
"%%Orientation: Landscape\n";
1214 next
if (/^
%%Pages
:\s
*\d
+/);
1217 if ($pno == 0 ||
$page =~
/\
)S
/) {
1218 # Header or single page containing text
1219 print
"%%Page: ? $pno\n" if ($pno > 0);
1223 # Empty page, skip it.
1230 # Skip from %%Trailer of one document to Endprolog
1231 # %%Page of the next
1232 $doprint = 0 if (/^
%%Trailer
/);
1233 $page .
= $_ if ($doprint);
1236 if ($page =~
/\
)S
/) {
1237 print
"%%Page: ? $pno\n";
1242 print
"%%Trailer\n%%Pages: $pno\n";
1245 $PERL /tmp
/$$.crmerge.pl
< $infile
1250 # input_cmd | insert_anchors | output_cmd
1252 # Flag blocks of difference with sequentially numbered invisible
1253 # anchors. These are used to drive the frames version of the
1256 # NOTE: Anchor zero flags the top of the file irrespective of changes,
1257 # an additional anchor is also appended to flag the bottom.
1259 # The script detects changed lines as any line that has a "<span
1260 # class=" string embedded (unchanged lines have no class set and are
1261 # not part of a <span>. Blank lines (without a sequence number)
1262 # are also detected since they flag lines that have been inserted or
1265 function insert_anchors
1269 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
1277 NF == 0 || /^<span class=/ {
1292 printf "<b style=\"font-size: large; color: red\">";
1293 printf "--- EOF ---</b>"
1294 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
1296 printf "<form name=\"eof\">";
1297 printf "<input name=\"value\" value=\"%d\" " \
1298 "type=\"hidden\"></input>", anc - 1;
1308 # Print a relative return path from $1 to $2. For example if
1309 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
1310 # this function would print "../../../../".
1312 # In the event that $1 is not in $2 a warning is printed to stderr,
1313 # and $2 is returned-- the result of this is that the resulting webrev
1314 # is not relocatable.
1316 function relative_dir
1318 typeset cur
="${1##$2?(/)}"
1321 # If the first path was specified absolutely, and it does
1322 # not start with the second path, it's an error.
1324 if [[ "$cur" = "/${1#/}" ]]; then
1325 # Should never happen.
1326 print
-u2 "\nWARNING: relative_dir: \"$1\" not relative "
1327 print
-u2 "to \"$2\". Check input paths. Framed webrev "
1328 print
-u2 "will not be relocatable!"
1334 # This is kind of ugly. The sed script will do the following:
1336 # 1. Strip off a leading "." or "./": this is important to get
1337 # the correct arcnav links for files in $WDIR.
1338 # 2. Strip off a trailing "/": this is not strictly necessary,
1339 # but is kind of nice, since it doesn't end up in "//" at
1340 # the end of a relative path.
1341 # 3. Replace all remaining sequences of non-"/" with "..": the
1342 # assumption here is that each dirname represents another
1343 # level of relative separation.
1344 # 4. Append a trailing "/" only for non-empty paths: this way
1345 # the caller doesn't need to duplicate this logic, and does
1346 # not end up using $RTOP/file for files in $WDIR.
1348 print
$cur |
$SED -e '{
1359 # Emit javascript for frame navigation
1361 function frame_nav_js
1369 function scrollByPix
()
1375 parent.lhs.scrollBy
(0, sfactor
);
1376 parent.rhs.scrollBy
(0, sfactor
);
1380 function scrollToAnc
(num
)
1382 // Update the value of the anchor
in the form
which we use as
1383 // storage
for this value. setAncValue
() will take care of
1384 // correcting
for overflow and underflow of the value and
return
1385 // us the new value.
1386 num
= setAncValue
(num
);
1388 // Set location and scroll back a little to expose previous
1391 // Note that this could be improved
: it is possible although
1392 // complex to compute the x and y position of an anchor
, and to
1393 // scroll to that location directly.
1395 parent.lhs.location.replace
(parent.lhs.location.pathname
+ "#" + num
);
1396 parent.rhs.location.replace
(parent.rhs.location.pathname
+ "#" + num
);
1398 parent.lhs.scrollBy
(0, -30);
1399 parent.rhs.scrollBy
(0, -30);
1402 function getAncValue
()
1404 return (parseInt
(parent.nav.document.
diff.real.value
));
1407 function setAncValue
(val
)
1411 parent.nav.document.
diff.real.value
= val
;
1412 parent.nav.document.
diff.display.value
= "BOF";
1417 // The way we compute the max anchor value is to stash it
1418 // inline
in the left and right hand side pages-- it
's the same
1419 // on each side, so we pluck from the left.
1421 maxval = parent.lhs.document.eof.value.value;
1423 parent.nav.document.diff.real.value = val;
1424 parent.nav.document.diff.display.value = val.toString();
1428 // this must be: val >= maxval
1430 parent.nav.document.diff.real.value = val;
1431 parent.nav.document.diff.display.value = "EOF";
1435 function stopScroll()
1437 if (scrolling == 1) {
1438 clearInterval(myInt);
1443 function startScroll()
1447 myInt = setInterval("scrollByPix()", 10);
1450 function handlePress(b)
1457 scrollToAnc(getAncValue() - 1);
1468 scrollToAnc(getAncValue() + 1);
1471 scrollToAnc(999999);
1476 function handleRelease(b)
1481 function keypress(ev)
1486 if (window.event) { // IE
1487 keynum = ev.keyCode;
1488 } else if (ev.which) { // non-IE
1492 keychar = String.fromCharCode(keynum);
1494 if (keychar == "k") {
1497 } else if (keychar == "j" || keychar == " ") {
1505 function ValidateDiffNum()
1510 val = parent.nav.document.diff.display.value;
1512 scrollToAnc(999999);
1523 parent.nav.document.diff.display.value = getAncValue();
1536 # Output anchor navigation file for framed sdiffs.
1538 function frame_navigation
1540 print "$HTML<head>$STDHEAD"
1543 <title>Anchor Navigation</title>
1544 <meta http-equiv="Content-Script-Type" content="text/javascript">
1545 <meta http-equiv="Content-Type" content="text/html">
1547 <style type="text/css">
1548 div.button td { padding-left: 5px; padding-right: 5px;
1549 background-color: #eee; text-align: center;
1550 border: 1px #444 outset; cursor: pointer; }
1551 div.button a { font-weight: bold; color: black }
1552 div.button td:hover { background: #ffcc99; }
1556 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
1560 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
1561 onkeypress="keypress(event);">
1562 <noscript lang="javascript">
1564 <p><big>Framed Navigation controls require Javascript</big><br></br>
1565 Either this browser is incompatable or javascript is not enabled</p>
1568 <table width="100%" border="0" align="center">
1570 <td valign="middle" width="25%">Diff navigation:
1571 Use 'j
' and 'k
' for next and previous diffs; or use buttons
1573 <td align="center" valign="top" width="50%">
1574 <div class="button">
1575 <table border="0" align="center">
1578 <a onMouseDown="handlePress(1);return true;"
1579 onMouseUp="handleRelease(1);return true;"
1580 onMouseOut="handleRelease(1);return true;"
1581 onClick="return false;"
1582 title="Go to Beginning Of file">BOF</a></td>
1584 <a onMouseDown="handlePress(3);return true;"
1585 onMouseUp="handleRelease(3);return true;"
1586 onMouseOut="handleRelease(3);return true;"
1587 title="Scroll Up: Press and Hold to accelerate"
1588 onClick="return false;">Scroll Up</a></td>
1590 <a onMouseDown="handlePress(2);return true;"
1591 onMouseUp="handleRelease(2);return true;"
1592 onMouseOut="handleRelease(2);return true;"
1593 title="Go to previous Diff"
1594 onClick="return false;">Prev Diff</a>
1599 <a onMouseDown="handlePress(6);return true;"
1600 onMouseUp="handleRelease(6);return true;"
1601 onMouseOut="handleRelease(6);return true;"
1602 onClick="return false;"
1603 title="Go to End Of File">EOF</a></td>
1605 <a onMouseDown="handlePress(4);return true;"
1606 onMouseUp="handleRelease(4);return true;"
1607 onMouseOut="handleRelease(4);return true;"
1608 title="Scroll Down: Press and Hold to accelerate"
1609 onClick="return false;">Scroll Down</a></td>
1611 <a onMouseDown="handlePress(5);return true;"
1612 onMouseUp="handleRelease(5);return true;"
1613 onMouseOut="handleRelease(5);return true;"
1614 title="Go to next Diff"
1615 onClick="return false;">Next Diff</a></td>
1620 <th valign="middle" width="25%">
1621 <form action="" name="diff" onsubmit="return ValidateDiffNum();">
1622 <input name="display" value="BOF" size="8" type="text"></input>
1623 <input name="real" value="0" size="8" type="hidden"></input>
1636 # diff_to_html <filename> <filepath> { U | C } <comment>
1638 # Processes the output of diff to produce an HTML file representing either
1639 # context or unified diffs.
1648 print "$HTML<head>$STDHEAD"
1649 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
1651 if [[ $DIFFTYPE == "U" ]]; then
1657 <body id="SUNWwebrev">
1658 <a class="print" href="javascript:print()">Print this page</a>
1665 /^\
+\
+\
+ new
/ { next
}
1667 /^\
*\
*\
* old
/ { next
}
1668 /^\
*\
*\
*\
*/ { next
}
1669 /^
-------/ { printf "<center><h1>%s</h1></center>\n", $0; next
}
1670 /^\@\@.
*\@\@$
/ { printf "</pre><hr></hr><pre>\n";
1671 printf "<span class=\"newmarker\">%s</span>\n", $0;
1674 /^\
*\
*\
*/ { printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0;
1676 /^
---/ { printf "<span class=\"newmarker\">%s</span>\n", $0;
1678 /^\
+/ {printf "<span class=\"new\">%s</span>\n", $0; next
}
1679 /^
!/ {printf "<span class=\"changed\">%s</span>\n", $0; next
}
1680 /^
-/ {printf "<span class=\"removed\">%s</span>\n", $0; next
}
1681 {printf "%s\n", $0; next
}
1684 print "</pre></body></html>\n"
1689 # source_to_html { new | old } <filename>
1691 # Process a plain vanilla source file to transform it into an HTML file.
1698 print "$HTML<head>$STDHEAD"
1699 print "<title>$WNAME $WHICH $TNAME</title>"
1700 print "<body id=\"SUNWwebrev\">"
1702 html_quote | $AWK '{line
+= 1 ; printf "%4d %s\n", line
, $0 }'
1703 print "</pre></body></html>"
1707 # comments_from_wx {text|html} filepath
1709 # Given the pathname of a file, find its location in a "wx" active
1710 # file list and print the following comment. Output is either text or
1711 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1712 # are turned into URLs.
1721 do getline
; while (NF
> 0)
1723 while (NF
> 0) { print
; getline
}
1727 if [[ -z $comm ]]; then
1728 comm="*** NO COMMENTS ***"
1731 if [[ $fmt == "text" ]]; then
1736 print -- "$comm" | html_quote | its2url
1741 # getcomments {text|html} filepath parentpath
1743 # Fetch the comments depending on what SCM mode we're
in.
1751 if [[ -n $Nflag ]]; then
1755 if [[ -n $wxfile ]]; then
1756 comments_from_wx
$fmt $p
1761 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1763 # Print out Code Inspection figures similar to sccs-prt(1) format.
1767 integer tot
=$1 ins
=$2 del
=$3 mod
=$4 unc
=$5
1769 if (( tot
== 1 )); then
1774 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \
1775 $tot $str $ins $del $mod $unc
1780 # difflines <oldfile> <newfile>
1782 # Calculate and emit number of added, removed, modified and unchanged lines,
1783 # and total lines changed, the sum of added + removed + modified.
1787 integer tot mod del ins unc err
1790 eval $
( diff -e $1 $2 |
$AWK '
1791 # Change range of lines: N,Nc
1792 /^[0-9]*,[0-9]*c$/ {
1793 n=split(substr($1,1,length($1)-1), counts, ",");
1799 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
1800 # following would be 5 - 3 = 2! Hence +1 for correction.
1802 r=(counts[2]-counts[1])+1;
1805 # Now count replacement lines: each represents a change instead
1806 # of a delete, so increment c and decrement r.
1808 while (getline != /^\.$/) {
1813 # If there were more replacement lines than original lines,
1814 # then r will be negative; in this case there are no deletions,
1815 # but there are r changes that should be counted as adds, and
1816 # since r is negative, subtract it from a and add it to c.
1824 # If there were more original lines than replacement lines, then
1825 # r will be positive; in this case, increment d by that much.
1835 # The first line is a replacement; any more are additions.
1836 if (getline != /^\.$/) {
1838 while (getline != /^\.$/) a++;
1843 # Add lines: both Na and N,Na
1845 while (getline != /^\.$/) a++;
1849 # Delete range of lines: N,Nd
1850 /^[0-9]*,[0-9]*d$/ {
1851 n=split(substr($1,1,length($1)-1), counts, ",");
1857 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
1858 # following would be 5 - 3 = 2! Hence +1 for correction.
1860 r=(counts[2]-counts[1])+1;
1865 # Delete line: Nd. For example 10d says line 10 is deleted.
1866 /^[0-9]*d$/ {d++; next}
1868 # Should not get here!
1874 # Finish off - print results
1876 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
1877 (c+d+a), c, d, a, error);
1880 # End of $AWK, Check to see if any trouble occurred.
1881 if (( $?
> 0 || err
> 0 )); then
1882 print
"Unexpected Error occurred reading" \
1883 "\`diff -e $1 $2\`: \$?=$?, err=" $err
1892 # Calculate unchanged lines
1894 if (( unc
> 0 )); then
1895 (( unc
-= del
+ mod
))
1899 print
"<span class=\"lineschanged\">"
1900 printCI
$tot $ins $del $mod $unc
1908 # Sets up webrev to source its information from a wx-formatted file.
1909 # Sets the global 'wxfile' variable.
1911 function flist_from_wx
1914 if [[ -n ${argfile%%/*} ]]; then
1916 # If the wx file pathname is relative then make it absolute
1917 # because the webrev does a "cd" later on.
1919 wxfile
=$PWD/$argfile
1924 $AWK '{ c = 1; print;
1926 if (NF == 0) { c = -c; continue }
1935 # Transform a specified 'git log' output format into a wx-like active list.
1942 TMPFLIST
=/tmp
/$$.active
1943 $PERL -e 'my (%files, %realfiles, $msg);
1944 my $parent = $ARGV[0];
1945 my $child = $ARGV[1];
1947 open(F, "git diff -M --name-status $parent..$child |");
1950 if (/^R(\d+)\s+([^ ]+)\s+([^ ]+)/) { # rename
1951 if ($1 >= 75) { # Probably worth treating as a rename
1952 $realfiles{$3} = $2;
1954 $realfiles{$3} = $3;
1955 $realfiles{$2} = $2;
1958 my $f = (split /\s+/, $_)[1];
1959 $realfiles{$f} = $f;
1964 my $state = 1; # 0|comments, 1|files
1965 open(F, "git whatchanged --pretty=format:%B $parent..$child |");
1969 my ($unused, $fname, $fname2) = split(/\t/, $_);
1970 $fname = $fname2 if defined($fname2);
1971 next if !defined($realfiles{$fname}); # No real change
1974 $files{$fname} .= $msg;
1978 $msg = /^\n/ ? "" : "\n";
1980 $msg .= "$_\n" if ($_);
1985 for (sort keys %files) {
1986 if ($realfiles{$_} ne $_) {
1987 print "$_ $realfiles{$_}\n$files{$_}\n\n";
1989 print "$_\n$files{$_}\n\n"
1991 }' ${parent} ${child} > $TMPFLIST
1998 # Build a wx-style active list, and hand it off to flist_from_wx
2000 function flist_from_git
2005 print
" File list from: git ...\c"
2006 git_wxfile
"$child" "$parent";
2008 # flist_from_wx prints the Done, so we don't have to.
2009 flist_from_wx
$TMPFLIST
2013 # flist_from_subversion
2015 # Generate the file list by extracting file names from svn status.
2017 function flist_from_subversion
2023 print
-u2 " File list from: svn status ... \c"
2024 svn status |
$AWK '/^[ACDMR]/ { print $NF }' > $FLIST
2029 function env_from_flist
2031 [[ -r $FLIST ]] ||
return
2034 # Use "eval" to set env variables that are listed in the file
2035 # list. Then copy those into our local versions of those
2036 # variables if they have not been set already.
2038 eval `$SED -e "s/#.*$//" $FLIST | $GREP = `
2040 if [[ -z $codemgr_ws && -n $CODEMGR_WS ]]; then
2041 codemgr_ws
=$CODEMGR_WS
2046 # Check to see if CODEMGR_PARENT is set in the flist file.
2048 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2049 codemgr_parent
=$CODEMGR_PARENT
2050 export CODEMGR_PARENT
2054 function look_for_prog
2061 ppath
=$ppath:/usr
/sfw
/bin
:/usr
/bin
:/usr
/sbin
2062 ppath
=$ppath:/opt
/onbld
/bin
2063 ppath
=$ppath:/opt
/onbld
/bin
/`uname -p`
2065 PATH
=$ppath prog
=`whence $progname`
2066 if [[ -n $prog ]]; then
2071 function get_file_mode
2074 if (@stat = stat($ARGV[0])) {
2075 $mode = $stat[2] & 0777;
2076 printf "%03o\n", $mode;
2084 function build_old_new_git
2099 # Get old file and its mode from the git object tree
2101 if [[ "$PDIR" == "." ]]; then
2107 if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then
2108 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2110 $GIT ls-tree
$GIT_PARENT $file |
read o_mode
type o_object junk
2111 $GIT cat-file
$type $o_object > $olddir/$file 2>/dev
/null
2113 if (( $?
!= 0 )); then
2115 elif [[ -n $o_mode ]]; then
2116 # Strip the first 3 digits, to get a regular octal mode
2117 o_mode
=${o_mode/???/}
2118 chmod $o_mode $olddir/$file
2120 # should never happen
2121 print
-u2 "ERROR: set mode of $olddir/$file"
2126 # new version of the file.
2128 if [[ "$DIR" == "." ]]; then
2133 rm -rf $newdir/$file
2135 if [[ -e $CWS/$DIR/$F ]]; then
2136 cp $CWS/$DIR/$F $newdir/$DIR/$F
2137 chmod $
(get_file_mode
$CWS/$DIR/$F) $newdir/$DIR/$F
2142 function build_old_new_subversion
2147 # Snag new version of file.
2148 rm -f $newdir/$DIR/$F
2149 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F
2151 if [[ -n $PWS && -e $PWS/$PDIR/$PF ]]; then
2152 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2154 # Get the parent's version of the file.
2155 svn status
$CWS/$DIR/$F |
read stat
file
2156 if [[ $stat != "A" ]]; then
2157 svn
cat -r BASE
$CWS/$DIR/$F > $olddir/$PDIR/$PF
2162 function build_old_new_unknown
2168 # Snag new version of file.
2170 rm -f $newdir/$DIR/$F
2171 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F
2174 # Snag the parent's version of the file.
2176 if [[ -f $PWS/$PDIR/$PF ]]; then
2177 rm -f $olddir/$PDIR/$PF
2178 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2182 function build_old_new
2192 typeset olddir
="$WDIR/raw_files/old"
2193 typeset newdir
="$WDIR/raw_files/new"
2195 mkdir
-p $olddir/$PDIR
2196 mkdir
-p $newdir/$DIR
2198 if [[ $SCM_MODE == "git" ]]; then
2199 build_old_new_git
"$olddir" "$newdir"
2200 elif [[ $SCM_MODE == "subversion" ]]; then
2201 build_old_new_subversion
"$olddir" "$newdir"
2202 elif [[ $SCM_MODE == "unknown" ]]; then
2203 build_old_new_unknown
"$olddir" "$newdir"
2206 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2207 print
"*** Error: file not in parent or child"
2219 print
'Usage:\twebrev [common-options]
2220 webrev [common-options] ( <file> | - )
2221 webrev [common-options] -w <wx file>
2224 -c <revision>: generate webrev for single revision (git only)
2225 -C <filename>: Use <filename> for the information tracking configuration.
2226 -D: delete remote webrev
2227 -h <revision>: specify "head" revision for comparison (git only)
2228 -i <filename>: Include <filename> in the index.html file.
2229 -I <filename>: Use <filename> for the information tracking registry.
2230 -n: do not generate the webrev (useful with -U)
2231 -O: Print bugids/arc cases suitable for OpenSolaris.
2232 -o <outdir>: Output webrev to specified directory.
2233 -p <compare-against>: Use specified parent wkspc or basis for comparison
2234 -t <remote_target>: Specify remote destination for webrev upload
2235 -U: upload the webrev to remote destination
2236 -w <wxfile>: Use specified wx active file.
2239 WDIR: Control the output directory.
2240 WEBREV_TRASH_DIR: Set directory for webrev delete.
2243 CODEMGR_WS: Workspace location.
2244 CODEMGR_PARENT: Parent workspace location.
2252 # Main program starts here
2256 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2260 PATH
=$
(/bin
/dirname "$(whence $0)"):$PATH
2262 [[ -z $WDIFF ]] && WDIFF
=`look_for_prog wdiff`
2263 [[ -z $WX ]] && WX
=`look_for_prog wx`
2264 [[ -z $GIT ]] && GIT
=`look_for_prog git`
2265 [[ -z $WHICH_SCM ]] && WHICH_SCM
=`look_for_prog which_scm`
2266 [[ -z $CODEREVIEW ]] && CODEREVIEW
=`look_for_prog codereview`
2267 [[ -z $PS2PDF ]] && PS2PDF
=`look_for_prog ps2pdf`
2268 [[ -z $PERL ]] && PERL
=`look_for_prog perl`
2269 [[ -z $RSYNC ]] && RSYNC
=`look_for_prog rsync`
2270 [[ -z $SCCS ]] && SCCS
=`look_for_prog sccs`
2271 [[ -z $AWK ]] && AWK
=`look_for_prog nawk`
2272 [[ -z $AWK ]] && AWK
=`look_for_prog gawk`
2273 [[ -z $AWK ]] && AWK
=`look_for_prog awk`
2274 [[ -z $SCP ]] && SCP
=`look_for_prog scp`
2275 [[ -z $SED ]] && SED
=`look_for_prog sed`
2276 [[ -z $SFTP ]] && SFTP
=`look_for_prog sftp`
2277 [[ -z $SORT ]] && SORT
=`look_for_prog sort`
2278 [[ -z $MKTEMP ]] && MKTEMP
=`look_for_prog mktemp`
2279 [[ -z $GREP ]] && GREP
=`look_for_prog grep`
2280 [[ -z $FIND ]] && FIND
=`look_for_prog find`
2281 [[ -z $MANDOC ]] && MANDOC
=`look_for_prog mandoc`
2282 [[ -z $COL ]] && COL
=`look_for_prog col`
2284 # set name of trash directory for remote webrev deletion
2286 [[ -n $WEBREV_TRASH_DIR ]] && TRASH_DIR
=$WEBREV_TRASH_DIR
2288 if [[ ! -x $PERL ]]; then
2289 print
-u2 "Error: No perl interpreter found. Exiting."
2293 if [[ ! -x $WHICH_SCM ]]; then
2294 print
-u2 "Error: Could not find which_scm. Exiting."
2299 # These aren't fatal, but we want to note them to the user.
2300 # We don't warn on the absence of 'wx' until later when we've
2301 # determined that we actually need to try to invoke it.
2303 [[ ! -x $CODEREVIEW ]] && print
-u2 "WARNING: codereview(1) not found."
2304 [[ ! -x $PS2PDF ]] && print
-u2 "WARNING: ps2pdf(1) not found."
2305 [[ ! -x $WDIFF ]] && print
-u2 "WARNING: wdiff not found."
2307 # Declare global total counters.
2308 integer TOTL TINS TDEL TMOD TUNC
2310 # default remote host for upload/delete
2311 typeset
-r DEFAULT_REMOTE_HOST
="cr.opensolaris.org"
2312 # prefixes for upload targets
2313 typeset
-r rsync_prefix
="rsync://"
2314 typeset
-r ssh_prefix
="ssh://"
2336 while getopts "c:C:Dh:i:I:lnNo:Op:t:Uw" opt
2340 codemgr_head
=$OPTARG
2341 codemgr_parent
=$OPTARG~
1;;
2349 codemgr_head
=$OPTARG;;
2352 INCLUDE_FILE
=$OPTARG;;
2364 # Strip the trailing slash to correctly form remote target.
2368 codemgr_parent
=$OPTARG;;
2371 remote_target
=$OPTARG;;
2383 if [[ -n $wflag && -n $lflag ]]; then
2387 # more sanity checking
2388 if [[ -n $nflag && -z $Uflag ]]; then
2389 print
"it does not make sense to skip webrev generation" \
2394 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2395 echo "remote target has to be used only for upload or delete"
2400 # For the invocation "webrev -n -U" with no other options, webrev will assume
2401 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2402 # $(basename ${CWS}). So we need to get CWS set before we skip any remaining
2405 $WHICH_SCM |
read SCM_MODE junk ||
exit 1
2407 if [[ $SCM_MODE == "git" ]]; then
2410 # 1. git rev-parse --git-dir from CODEMGR_WS environment variable
2411 # 2. git rev-parse --git-dir from directory of invocation
2413 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && \
2414 codemgr_ws
=$
($GIT --git-dir=$CODEMGR_WS/.git rev-parse
--git-dir \
2416 [[ -z $codemgr_ws ]] && \
2417 codemgr_ws
=$
($GIT rev-parse
--git-dir 2>/dev
/null
)
2419 if [[ "$codemgr_ws" == ".git" ]]; then
2420 codemgr_ws
="${PWD}/${codemgr_ws}"
2423 if [[ "$codemgr_ws" = *"/.git" ]]; then
2424 codemgr_ws
=$
(dirname $codemgr_ws) # Lose the '/.git'
2427 elif [[ $SCM_MODE == "subversion" ]]; then
2429 # Subversion priorities:
2430 # 1. CODEMGR_WS from environment
2431 # 2. Relative path from current directory to SVN repository root
2433 if [[ -n $CODEMGR_WS && -d $CODEMGR_WS/.svn
]]; then
2436 svn info |
while read line
; do
2437 if [[ $line == "URL: "* ]]; then
2439 elif [[ $line == "Repository Root: "* ]]; then
2440 repo
=${line#Repository Root: }
2450 # If no SCM has been determined, take either the environment setting
2451 # setting for CODEMGR_WS, or the current directory if that wasn't set.
2453 if [[ -z ${CWS} ]]; then
2454 CWS
=${CODEMGR_WS:-.}
2458 # If the command line options indicate no webrev generation, either
2459 # explicitly (-n) or implicitly (-D but not -U), then there's a whole
2460 # ton of logic we can skip.
2462 # Instead of increasing indentation, we intentionally leave this loop
2463 # body open here, and exit via break from multiple points within.
2464 # Search for DO_EVERYTHING below to find the break points and closure.
2466 for do_everything
in 1; do
2468 # DO_EVERYTHING: break point
2469 if [[ -n $nflag ||
( -z $Uflag && -n $Dflag ) ]]; then
2474 # If this manually set as the parent, and it appears to be an earlier webrev,
2475 # then note that fact and set the parent to the raw_files/new subdirectory.
2477 if [[ -n $pflag && -d $codemgr_parent/raw_files
/new
]]; then
2478 parent_webrev
=$
(readlink
-f "$codemgr_parent")
2479 codemgr_parent
=$
(readlink
-f "$codemgr_parent/raw_files/new")
2482 if [[ -z $wflag && -z $lflag ]]; then
2483 shift $
(($OPTIND - 1))
2485 if [[ $1 == "-" ]]; then
2490 elif [[ -n $1 ]]; then
2491 if [[ ! -r $1 ]]; then
2492 print
-u2 "$1: no such file or not readable"
2506 # Before we go on to further consider -l and -w, work out which SCM we think
2513 if [[ $flist_mode == "auto" ]]; then
2514 print
-u2 "Unable to determine SCM in use and file list not specified"
2515 print
-u2 "See which_scm(1) for SCM detection information."
2520 if [[ $flist_mode == "auto" ]]; then
2521 print
-u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2527 print
-u2 " SCM detected: $SCM_MODE"
2529 if [[ -n $wflag ]]; then
2531 # If the -w is given then assume the file list is in Bonwick's "wx"
2532 # command format, i.e. pathname lines alternating with SCCS comment
2533 # lines with blank lines as separators. Use the SCCS comments later
2534 # in building the index.html file.
2536 shift $
(($OPTIND - 1))
2538 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2539 if [[ -r $CODEMGR_WS/wx
/active
]]; then
2540 wxfile
=$CODEMGR_WS/wx
/active
2544 [[ -z $wxfile ]] && print
-u2 "wx file not specified, and could not " \
2545 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2547 if [[ ! -r $wxfile ]]; then
2548 print
-u2 "$wxfile: no such file or not readable"
2552 print
-u2 " File list from: wx 'active' file '$wxfile' ... \c"
2553 flist_from_wx
$wxfile
2555 if [[ -n "$*" ]]; then
2558 elif [[ $flist_mode == "stdin" ]]; then
2559 print
-u2 " File list from: standard input"
2560 elif [[ $flist_mode == "file" ]]; then
2561 print
-u2 " File list from: $flist_file"
2564 if [[ $# -gt 0 ]]; then
2565 print
-u2 "WARNING: unused arguments: $*"
2569 if [[ $SCM_MODE == "git" ]]; then
2570 # Check that "head" revision specified with -c or -h is sane
2571 if [[ -n $cflag ||
-n $hflag ]]; then
2572 head_rev
=$
($GIT rev-parse
--verify --quiet "$codemgr_head")
2573 if [[ -z $head_rev ]]; then
2574 print
-u2 "Error: bad revision ${codemgr_head}"
2579 if [[ -z $codemgr_head ]]; then
2580 codemgr_head
="HEAD";
2583 # Parent can either be specified with -p, or specified with
2584 # CODEMGR_PARENT in the environment.
2585 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2586 codemgr_parent
=$CODEMGR_PARENT
2589 # Try to figure out the parent based on the branch the current
2590 # branch is tracking, if we fail, use origin/master
2591 this_branch
=$
($GIT branch | nawk
'$1 == "*" { print $2 }')
2592 par_branch
="origin/master"
2594 # If we're not on a branch there's nothing we can do
2595 if [[ $this_branch != "(no branch)" ]]; then
2597 --format='%(refname:short) %(upstream:short)' \
2599 while read local remote
; do
2600 if [[ "$local" == "$this_branch" ]]; then
2601 par_branch
="$remote"
2606 if [[ -z $codemgr_parent ]]; then
2607 codemgr_parent
=$par_branch
2612 # If the parent is a webrev, we want to do some things against
2613 # the natural workspace parent (file list, comments, etc)
2615 if [[ -n $parent_webrev ]]; then
2616 real_parent
=$par_branch
2621 if [[ -z $flist_done ]]; then
2622 flist_from_git
"$codemgr_head" "$real_parent"
2627 # If we have a file list now, pull out any variables set
2630 if [[ -n $flist_done ]]; then
2635 # If we don't have a wx-format file list, build one we can pull change
2638 if [[ -z $wxfile ]]; then
2639 print
" Comments from: git...\c"
2640 git_wxfile
"$codemgr_head" "$real_parent"
2644 if [[ -z $GIT_PARENT ]]; then
2645 GIT_PARENT
=$
($GIT merge-base
"$real_parent" "$codemgr_head")
2647 if [[ -z $GIT_PARENT ]]; then
2648 print
-u2 "Error: Cannot discover parent revision"
2652 pnode
=$
(trim_digest
$GIT_PARENT)
2654 if [[ -n $cflag ]]; then
2655 PRETTY_PWS
="previous revision (at ${pnode})"
2656 elif [[ $real_parent == */* ]]; then
2657 origin
=$
(echo $real_parent | cut
-d/ -f1)
2658 origin
=$
($GIT remote
-v | \
2659 $AWK '$1 == "'$origin'" { print $2; exit }')
2660 PRETTY_PWS
="${PWS} (${origin} at ${pnode})"
2661 elif [[ -n $pflag && -z $parent_webrev ]]; then
2662 PRETTY_PWS
="${CWS} (explicit revision ${pnode})"
2664 PRETTY_PWS
="${PWS} (at ${pnode})"
2667 cnode
=$
($GIT --git-dir=${codemgr_ws}/.git rev-parse
--short=12 \
2668 ${codemgr_head} 2>/dev
/null
)
2670 if [[ -n $cflag ||
-n $hflag ]]; then
2671 PRETTY_CWS
="${CWS} (explicit head at ${cnode})"
2673 PRETTY_CWS
="${CWS} (at ${cnode})"
2675 elif [[ $SCM_MODE == "subversion" ]]; then
2678 # We only will have a real parent workspace in the case one
2679 # was specified (be it an older webrev, or another checkout).
2681 [[ -n $codemgr_parent ]] && PWS
=$codemgr_parent
2683 if [[ -z $flist_done && $flist_mode == "auto" ]]; then
2684 flist_from_subversion
$CWS $OLDPWD
2687 if [[ $SCM_MODE == "unknown" ]]; then
2688 print
-u2 " Unknown type of SCM in use"
2690 print
-u2 " Unsupported SCM in use: $SCM_MODE"
2695 if [[ -z $CODEMGR_WS ]]; then
2696 print
-u2 "SCM not detected/supported and " \
2697 "CODEMGR_WS not specified"
2701 if [[ -z $CODEMGR_PARENT ]]; then
2702 print
-u2 "SCM not detected/supported and " \
2703 "CODEMGR_PARENT not specified"
2712 # If the user didn't specify a -i option, check to see if there is a
2713 # webrev-info file in the workspace directory.
2715 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2717 INCLUDE_FILE
="$CWS/webrev-info"
2720 if [[ -n $iflag ]]; then
2721 if [[ ! -r $INCLUDE_FILE ]]; then
2722 print
-u2 "include file '$INCLUDE_FILE' does not exist or is" \
2727 # $INCLUDE_FILE may be a relative path, and the script alters
2728 # PWD, so we just stash a copy in /tmp.
2730 cp $INCLUDE_FILE /tmp
/$$.include
2734 # DO_EVERYTHING: break point
2735 if [[ -n $Nflag ]]; then
2740 typeset
-r its_sed_script
=/tmp
/$$.its_sed
2742 if [[ -z $nflag ]]; then
2743 DEFREGFILE
="$(/bin/dirname "$
(whence
$0)")/../etc/its.reg"
2744 if [[ -n $Iflag ]]; then
2746 elif [[ -r $HOME/.its.reg
]]; then
2747 REGFILE
=$HOME/.its.reg
2751 if [[ ! -r $REGFILE ]]; then
2752 print
"ERROR: Unable to read database registry file $REGFILE"
2754 elif [[ $REGFILE != $DEFREGFILE ]]; then
2755 print
" its.reg from: $REGFILE"
2758 $SED -e '/^#/d' -e '/^[ ]*$/d' $REGFILE |
while read LINE
; do
2763 if [[ $name == PREFIX
]]; then
2765 valid_prefixes
="${p} ${valid_prefixes}"
2767 itsinfo
["${p}_${name}"]="${value}"
2772 DEFCONFFILE
="$(/bin/dirname "$
(whence
$0)")/../etc/its.conf"
2773 CONFFILES
=$DEFCONFFILE
2774 if [[ -r $HOME/.its.conf
]]; then
2775 CONFFILES
="${CONFFILES} $HOME/.its.conf"
2777 if [[ -n $Cflag ]]; then
2778 CONFFILES
="${CONFFILES} ${ITSCONF}"
2782 for cf
in ${CONFFILES}; do
2783 if [[ ! -r $cf ]]; then
2784 print
"ERROR: Unable to read database configuration file $cf"
2786 elif [[ $cf != $DEFCONFFILE ]]; then
2787 print
" its.conf: reading $cf"
2789 $SED -e '/^#/d' -e '/^[ ]*$/d' $cf |
while read LINE
; do
2795 # If an information tracking system is explicitly identified by prefix,
2796 # we want to disregard the specified priorities and resolve it accordingly.
2798 # To that end, we'll build a sed script to do each valid prefix in turn.
2800 for p
in ${valid_prefixes}; do
2802 # When an informational URL was provided, translate it to a
2803 # hyperlink. When omitted, simply use the prefix text.
2805 if [[ -z ${itsinfo["${p}_INFO"]} ]]; then
2806 itsinfo
["${p}_INFO"]=${p}
2808 itsinfo
["${p}_INFO"]="<a href=\\\"${itsinfo["${p}_INFO"]}\\\">${p}</a>"
2812 # Assume that, for this invocation of webrev, all references
2813 # to this information tracking system should resolve through
2816 # If the caller specified -O, then always use EXTERNAL_URL.
2818 # Otherwise, look in the list of domains for a matching
2821 [[ -z $Oflag ]] && for d
in ${its_domain}; do
2822 if [[ -n ${itsinfo["${p}_INTERNAL_URL_${d}"]} ]]; then
2823 itsinfo
["${p}_URL"]="${itsinfo[${p}_INTERNAL_URL_${d}]}"
2827 if [[ -z ${itsinfo["${p}_URL"]} ]]; then
2828 itsinfo
["${p}_URL"]="${itsinfo[${p}_EXTERNAL_URL]}"
2832 # Turn the destination URL into a hyperlink
2834 itsinfo
["${p}_URL"]="<a href=\\\"${itsinfo[${p}_URL]}\\\">&</a>"
2836 # The character class below contains a literal tab
2837 print
"/^${p}[: ]/ {
2838 s;${itsinfo[${p}_REGEX]};${itsinfo[${p}_URL]};g
2839 s;^${p};${itsinfo[${p}_INFO]};
2840 }" >> ${its_sed_script}
2844 # The previous loop took care of explicit specification. Now use
2845 # the configured priorities to attempt implicit translations.
2847 for p
in ${its_priority}; do
2848 print
"/^${itsinfo[${p}_REGEX]}[ ]/ {
2849 s;^${itsinfo[${p}_REGEX]};${itsinfo[${p}_URL]};g
2850 }" >> ${its_sed_script}
2855 # Search for DO_EVERYTHING above for matching "for" statement
2856 # and explanation of this terminator.
2863 WDIR
=${WDIR:-$CWS/webrev}
2866 # Name of the webrev, derived from the workspace name or output directory;
2867 # in the future this could potentially be an option.
2869 if [[ -n $oflag ]]; then
2875 # Make sure remote target is well formed for remote upload/delete.
2876 if [[ -n $Dflag ||
-n $Uflag ]]; then
2878 # If remote target is not specified, build it from scratch using
2879 # the default values.
2881 if [[ -z $tflag ]]; then
2882 remote_target
=${DEFAULT_REMOTE_HOST}:${WNAME}
2885 # Check upload target prefix first.
2887 if [[ "${remote_target}" != ${rsync_prefix}* &&
2888 "${remote_target}" != ${ssh_prefix}* ]]; then
2889 print
"ERROR: invalid prefix of upload URI" \
2894 # If destination specification is not in the form of
2895 # host_spec:remote_dir then assume it is just remote hostname
2896 # and append a colon and destination directory formed from
2897 # local webrev directory name.
2899 typeset target_no_prefix
=${remote_target##*://}
2900 if [[ ${target_no_prefix} == *:* ]]; then
2901 if [[ "${remote_target}" == *: ]]; then
2902 remote_target
=${remote_target}${WNAME}
2905 if [[ ${target_no_prefix} == */* ]]; then
2906 print
"ERROR: badly formed upload URI" \
2910 remote_target
=${remote_target}:${WNAME}
2916 # Strip trailing slash. Each upload method will deal with directory
2917 # specification separately.
2919 remote_target
=${remote_target%/}
2923 # Option -D by itself (option -U not present) implies no webrev generation.
2925 if [[ -z $Uflag && -n $Dflag ]]; then
2931 # Do not generate the webrev, just upload it or delete it.
2933 if [[ -n $nflag ]]; then
2934 if [[ -n $Dflag ]]; then
2936 (( $?
== 0 )) ||
exit $?
2938 if [[ -n $Uflag ]]; then
2944 if [ "${WDIR%%/*}" ]; then
2948 if [[ ! -d $WDIR ]]; then
2950 (( $?
!= 0 )) && exit 1
2954 # Summarize what we're going to do.
2956 print
" Workspace: ${PRETTY_CWS:-$CWS}"
2957 if [[ -n $parent_webrev ]]; then
2958 print
"Compare against: webrev at $parent_webrev"
2960 print
"Compare against: ${PRETTY_PWS:-$PWS}"
2963 [[ -n $INCLUDE_FILE ]] && print
" Including: $INCLUDE_FILE"
2964 print
" Output to: $WDIR"
2967 # Save the file list in the webrev dir
2969 [[ ! $FLIST -ef $WDIR/file.list
]] && cp $FLIST $WDIR/file.list
2971 rm -f $WDIR/$WNAME.
patch
2972 rm -f $WDIR/$WNAME.ps
2973 rm -f $WDIR/$WNAME.pdf
2975 touch $WDIR/$WNAME.
patch
2977 print
" Output Files:"
2980 # Clean up the file list: Remove comments, blank lines and env variables.
2982 $SED -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp
/$$.flist.clean
2983 FLIST
=/tmp
/$$.flist.clean
2986 # First pass through the files: generate the per-file webrev HTML-files.
2988 cat $FLIST |
while read LINE
2994 # Normally, each line in the file list is just a pathname of a
2995 # file that has been modified or created in the child. A file
2996 # that is renamed in the child workspace has two names on the
2997 # line: new name followed by the old name.
3002 if [[ $# -eq 2 ]]; then
3003 PP
=$2 # old filename
3004 if [[ -f $PP ]]; then
3005 oldname
=" (copied from $PP)"
3007 oldname
=" (renamed from $PP)"
3012 if [[ $PDIR == $PP ]]; then
3013 PDIR
="." # File at root of workspace
3019 if [[ $DIR == $P ]]; then
3020 DIR
="." # File at root of workspace
3027 if [[ "$DIR" == "$P" ]]; then
3028 DIR
="." # File at root of workspace
3038 COMM
=`getcomments html $P $PP`
3040 print
"\t$P$oldname\n\t\t\c"
3042 # Make the webrev mirror directory if necessary
3046 # We stash old and new files into parallel directories in $WDIR
3047 # and do our diffs there. This makes it possible to generate
3048 # clean looking diffs which don't have absolute paths present.
3051 build_old_new
"$WDIR" "$PWS" "$PDIR" "$PF" "$CWS" "$DIR" "$F" || \
3055 # Keep the old PWD around, so we can safely switch back after
3056 # diff generation, such that build_old_new runs in a
3057 # consistent environment.
3063 # The "git apply" command does not tolerate the spurious
3064 # "./" that we otherwise insert; be careful not to include
3065 # it in the paths that we pass to diff(1).
3067 if [[ $PDIR == "." ]]; then
3072 if [[ $DIR == "." ]]; then
3079 cmp $ofile $nfile > /dev
/null
2>&1
3080 if [[ $?
== 0 && $rename == 1 ]]; then
3085 # If we have old and new versions of the file then run the appropriate
3086 # diffs. This is complicated by a couple of factors:
3088 # - renames must be handled specially: we emit a 'remove'
3089 # diff and an 'add' diff
3090 # - new files and deleted files must be handled specially
3091 # - GNU patch doesn't interpret the output of illumos diff
3092 # properly when it comes to adds and deletes. We need to
3093 # do some "cleansing" transformations:
3094 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@
3095 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@
3097 cleanse_rmfile
="$SED 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
3098 cleanse_newfile
="$SED 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
3100 rm -f $WDIR/$DIR/$F.
patch
3101 if [[ -z $rename ]]; then
3102 if [ ! -f "$ofile" ]; then
3103 diff -u /dev
/null
$nfile | sh
-c "$cleanse_newfile" \
3104 > $WDIR/$DIR/$F.
patch
3105 elif [ ! -f "$nfile" ]; then
3106 diff -u $ofile /dev
/null | sh
-c "$cleanse_rmfile" \
3107 > $WDIR/$DIR/$F.
patch
3109 diff -u $ofile $nfile > $WDIR/$DIR/$F.
patch
3112 diff -u $ofile /dev
/null | sh
-c "$cleanse_rmfile" \
3113 > $WDIR/$DIR/$F.
patch
3115 diff -u /dev
/null
$nfile | sh
-c "$cleanse_newfile" \
3116 >> $WDIR/$DIR/$F.
patch
3120 # Tack the patch we just made onto the accumulated patch for the
3123 cat $WDIR/$DIR/$F.
patch >> $WDIR/$WNAME.
patch
3126 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
3127 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
3128 diff_to_html
$F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
3129 > $WDIR/$DIR/$F.cdiff.html
3132 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
3133 diff_to_html
$F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
3134 > $WDIR/$DIR/$F.udiff.html
3137 if [[ -x $WDIFF ]]; then
3139 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
3140 $WDIR/$DIR/$F.wdiff.html
2>/dev
/null
3141 if [[ $?
-eq 0 ]]; then
3144 print
" wdiffs[fail]\c"
3148 sdiff_to_html
$ofile $nfile $F $DIR "$COMM" \
3149 > $WDIR/$DIR/$F.
sdiff.html
3153 rm -f $WDIR/$DIR/$F.cdiff
$WDIR/$DIR/$F.udiff
3154 difflines
$ofile $nfile > $WDIR/$DIR/$F.count
3155 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
3156 # renamed file: may also have differences
3157 difflines
$ofile $nfile > $WDIR/$DIR/$F.count
3158 elif [[ -f $nfile ]]; then
3159 # new file: count added lines
3160 difflines
/dev
/null
$nfile > $WDIR/$DIR/$F.count
3161 elif [[ -f $ofile ]]; then
3162 # old file: count deleted lines
3163 difflines
$ofile /dev
/null
> $WDIR/$DIR/$F.count
3167 # Check if it's man page, and create plain text, html and raw (ascii)
3168 # output for the new version, as well as diffs against old version.
3170 if [[ -f "$nfile" && "$nfile" = *.
+([0-9])*([a-zA-Z
]) && \
3171 -x $MANDOC && -x $COL ]]; then
3172 $MANDOC -Tascii $nfile |
$COL -b > $nfile.man.txt
3173 source_to_html txt
< $nfile.man.txt
> $nfile.man.txt.html
3175 print
"$MANCSS" > $WDIR/raw_files
/new
/$DIR/man.css
3176 $MANDOC -Thtml -Ostyle=man.css
$nfile > $nfile.man.html
3178 $MANDOC -Tascii $nfile > $nfile.man.raw
3180 if [[ -f "$ofile" && -z $mv_but_nodiff ]]; then
3181 $MANDOC -Tascii $ofile |
$COL -b > $ofile.man.txt
3182 ${CDIFFCMD:-diff -bt -C 5} $ofile.man.txt \
3183 $nfile.man.txt
> $WDIR/$DIR/$F.man.cdiff
3184 diff_to_html
$F $DIR/$F "C" "$COMM" < \
3185 $WDIR/$DIR/$F.man.cdiff
> \
3186 $WDIR/$DIR/$F.man.cdiff.html
3187 print
" man-cdiffs\c"
3188 ${UDIFFCMD:-diff -bt -U 5} $ofile.man.txt \
3189 $nfile.man.txt
> $WDIR/$DIR/$F.man.udiff
3190 diff_to_html
$F $DIR/$F "U" "$COMM" < \
3191 $WDIR/$DIR/$F.man.udiff
> \
3192 $WDIR/$DIR/$F.man.udiff.html
3193 print
" man-udiffs\c"
3194 if [[ -x $WDIFF ]]; then
3195 $WDIFF -c "$COMM" -t "$WNAME Wdiff $DIR/$F" \
3196 $ofile.man.txt
$nfile.man.txt
> \
3197 $WDIR/$DIR/$F.man.wdiff.html
2>/dev
/null
3198 if [[ $?
-eq 0 ]]; then
3199 print
" man-wdiffs\c"
3201 print
" man-wdiffs[fail]\c"
3204 sdiff_to_html
$ofile.man.txt
$nfile.man.txt
$F.man
$DIR \
3205 "$COMM" > $WDIR/$DIR/$F.man.
sdiff.html
3206 print
" man-sdiffs\c"
3207 print
" man-frames\c"
3209 rm -f $ofile.man.txt
$nfile.man.txt
3210 rm -f $WDIR/$DIR/$F.man.cdiff
$WDIR/$DIR/$F.man.udiff
3214 # Now we generate the postscript for this file. We generate diffs
3215 # only in the event that there is delta, or the file is new (it seems
3216 # tree-killing to print out the contents of deleted files).
3218 if [[ -f $nfile ]]; then
3220 [[ ! -f $ofile ]] && ocr
=/dev
/null
3222 if [[ -z $mv_but_nodiff ]]; then
3223 textcomm
=`getcomments text $P $PP`
3224 if [[ -x $CODEREVIEW ]]; then
3225 $CODEREVIEW -y "$textcomm" \
3227 > /tmp
/$$.psfile
2>/dev
/null
&&
3228 cat /tmp
/$$.psfile
>> $WDIR/$WNAME.ps
3229 if [[ $?
-eq 0 ]]; then
3238 if [[ -f $ofile ]]; then
3239 source_to_html Old
$PP < $ofile > $WDIR/$DIR/$F-.html
3243 if [[ -f $nfile ]]; then
3244 source_to_html New
$P < $nfile > $WDIR/$DIR/$F.html
3253 frame_nav_js
> $WDIR/ancnav.js
3254 frame_navigation
> $WDIR/ancnav.html
3256 if [[ ! -f $WDIR/$WNAME.ps
]]; then
3257 print
" Generating PDF: Skipped: no output available"
3258 elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then
3259 print
" Generating PDF: \c"
3260 fix_postscript
$WDIR/$WNAME.ps |
$PS2PDF - > $WDIR/$WNAME.pdf
3263 print
" Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'"
3266 # If we're in OpenSolaris mode and there's a closed dir under $WDIR,
3267 # delete it - prevent accidental publishing of closed source
3269 if [[ -n "$Oflag" ]]; then
3270 $FIND $WDIR -type d
-name closed
-exec /bin
/rm -rf {} \
;
3273 # Now build the index.html file that contains
3274 # links to the source files and their diffs.
3278 # Save total changed lines for Code Inspection.
3279 print
"$TOTL" > $WDIR/TotalChangedLines
3281 print
" index.html: \c"
3282 INDEXFILE
=$WDIR/index.html
3283 exec 3<&1 # duplicate stdout to FD3.
3284 exec 1<&- # Close stdout.
3285 exec > $INDEXFILE # Open stdout to index file.
3287 print
"$HTML<head>$STDHEAD"
3288 print
"<title>$WNAME</title>"
3290 print
"<body id=\"SUNWwebrev\">"
3291 print
"<div class=\"summary\">"
3292 print
"<h2>Code Review for $WNAME</h2>"
3297 # Get the preparer's name:
3299 # If the SCM detected is Git, and the configuration property user.name is
3300 # available, use that, but be careful to properly escape angle brackets (HTML
3301 # syntax characters) in the email address.
3303 # Otherwise, use the current userid in the form "John Doe (jdoe)", but
3304 # to maintain compatibility with passwd(4), we must support '&' substitutions.
3307 if [[ "$SCM_MODE" == git
]]; then
3308 preparer
=$
(git config user.name
2>/dev
/null
)
3309 if [[ -n "$preparer" ]]; then
3310 preparer
="$(echo "$preparer" | html_quote)"
3313 if [[ -z "$preparer" ]]; then
3316 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<);
3318 $gcos =~ s/\&/ucfirst($login)/e;
3319 printf "%s (%s)\n", $gcos, $login;
3321 printf "(unknown)\n";
3326 PREPDATE
=$
(LC_ALL
=C
/usr
/bin
/date +%Y-
%b-
%d\
%R\
%z\
%Z
)
3327 print
"<tr><th>Prepared by:</th><td>$preparer on $PREPDATE</td></tr>"
3328 print
"<tr><th>Workspace:</th><td>${PRETTY_CWS:-$CWS}"
3330 print
"<tr><th>Compare against:</th><td>"
3331 if [[ -n $parent_webrev ]]; then
3332 print
"webrev at $parent_webrev"
3334 print
"${PRETTY_PWS:-$PWS}"
3337 print
"<tr><th>Summary of changes:</th><td>"
3338 printCI
$TOTL $TINS $TDEL $TMOD $TUNC
3341 if [[ -f $WDIR/$WNAME.
patch ]]; then
3342 wpatch_url
="$(print $WNAME.patch | url_encode)"
3343 print
"<tr><th>Patch of changes:</th><td>"
3344 print
"<a href=\"$wpatch_url\">$WNAME.patch</a></td></tr>"
3346 if [[ -f $WDIR/$WNAME.pdf
]]; then
3347 wpdf_url
="$(print $WNAME.pdf | url_encode)"
3348 print
"<tr><th>Printable review:</th><td>"
3349 print
"<a href=\"$wpdf_url\">$WNAME.pdf</a></td></tr>"
3352 if [[ -n "$iflag" ]]; then
3353 print
"<tr><th>Author comments:</th><td><div>"
3355 print
"</div></td></tr>"
3361 # Second pass through the files: generate the rest of the index file
3363 cat $FLIST |
while read LINE
3368 if [[ $# == 2 ]]; then
3377 cmp $WDIR/raw_files
/old
/$PP $WDIR/raw_files
/new
/$P > /dev
/null
2>&1
3378 if [[ $?
== 0 && -n "$oldname" ]]; then
3383 if [[ $DIR == $P ]]; then
3384 DIR
="." # File at root of workspace
3387 # Avoid processing the same file twice.
3388 # It's possible for renamed files to
3389 # appear twice in the file list
3395 # If there's a diffs file, make diffs links
3397 if [[ -f $F.cdiff.html
]]; then
3398 cdiff_url
="$(print $P.cdiff.html | url_encode)"
3399 udiff_url
="$(print $P.udiff.html | url_encode)"
3400 sdiff_url
="$(print $P.sdiff.html | url_encode)"
3401 frames_url
="$(print $P.frames.html | url_encode)"
3402 print
"<a href=\"$cdiff_url\">Cdiffs</a>"
3403 print
"<a href=\"$udiff_url\">Udiffs</a>"
3404 if [[ -f $F.wdiff.html
&& -x $WDIFF ]]; then
3405 wdiff_url
="$(print $P.wdiff.html | url_encode)"
3406 print
"<a href=\"$wdiff_url\">Wdiffs</a>"
3408 print
"<a href=\"$sdiff_url\">Sdiffs</a>"
3409 print
"<a href=\"$frames_url\">Frames</a>"
3411 print
" ------ ------"
3412 if [[ -x $WDIFF ]]; then
3415 print
" ------ ------"
3418 # If there's an old file, make the link
3420 if [[ -f $F-.html
]]; then
3421 oldfile_url
="$(print $P-.html | url_encode)"
3422 print
"<a href=\"$oldfile_url\">Old</a>"
3427 # If there's an new file, make the link
3429 if [[ -f $F.html
]]; then
3430 newfile_url
="$(print $P.html | url_encode)"
3431 print
"<a href=\"$newfile_url\">New</a>"
3436 if [[ -f $F.
patch ]]; then
3437 patch_url
="$(print $P.patch | url_encode)"
3438 print
"<a href=\"$patch_url\">Patch</a>"
3443 if [[ -f $WDIR/raw_files
/new
/$P ]]; then
3444 rawfiles_url
="$(print raw_files/new/$P | url_encode)"
3445 print
"<a href=\"$rawfiles_url\">Raw</a>"
3452 # For renamed files, clearly state whether or not they are modified
3453 if [[ -f "$oldname" ]]; then
3454 if [[ -n "$mv_but_nodiff" ]]; then
3455 print
"<i>(copied from $oldname)</i>"
3457 print
"<i>(copied and modified from $oldname)</i>"
3459 elif [[ -n "$oldname" ]]; then
3460 if [[ -n "$mv_but_nodiff" ]]; then
3461 print
"<i>(renamed from $oldname)</i>"
3463 print
"<i>(renamed and modified from $oldname)</i>"
3467 # If there's an old file, but no new file, the file was deleted
3468 if [[ -f $F-.html
&& ! -f $F.html
]]; then
3469 print
" <i>(deleted)</i>"
3472 # Check for usr/closed and deleted_files/usr/closed
3473 if [ ! -z "$Oflag" ]; then
3474 if [[ $P == usr
/closed
/* || \
3475 $P == deleted_files
/usr
/closed
/* ]]; then
3476 print
" <i>Closed source: omitted from" \
3482 if [[ -f $F.man.cdiff.html || \
3483 -f $WDIR/raw_files
/new
/$P.man.txt.html
]]; then
3488 if [[ -f $F.man.cdiff.html
]]; then
3489 mancdiff_url
="$(print $P.man.cdiff.html | url_encode)"
3490 manudiff_url
="$(print $P.man.udiff.html | url_encode)"
3491 mansdiff_url
="$(print $P.man.sdiff.html | url_encode)"
3492 manframes_url
="$(print $P.man.frames.html | url_encode)"
3493 print
"<a href=\"$mancdiff_url\">Cdiffs</a>"
3494 print
"<a href=\"$manudiff_url\">Udiffs</a>"
3495 if [[ -f $F.man.wdiff.html
&& -x $WDIFF ]]; then
3496 manwdiff_url
="$(print $P.man.wdiff.html | url_encode)"
3497 print
"<a href=\"$manwdiff_url\">Wdiffs</a>"
3499 print
"<a href=\"$mansdiff_url\">Sdiffs</a>"
3500 print
"<a href=\"$manframes_url\">Frames</a>"
3501 elif [[ -n $manpage ]]; then
3502 print
" ------ ------"
3503 if [[ -x $WDIFF ]]; then
3506 print
" ------ ------"
3509 if [[ -f $WDIR/raw_files
/new
/$P.man.txt.html
]]; then
3510 mantxt_url
="$(print raw_files/new/$P.man.txt.html | url_encode)"
3511 print
"<a href=\"$mantxt_url\">TXT</a>"
3512 manhtml_url
="$(print raw_files/new/$P.man.html | url_encode)"
3513 print
"<a href=\"$manhtml_url\">HTML</a>"
3514 manraw_url
="$(print raw_files/new/$P.man.raw | url_encode)"
3515 print
"<a href=\"$manraw_url\">Raw</a>"
3516 elif [[ -n $manpage ]]; then
3517 print
" --- ---- ---"
3522 # Insert delta comments
3523 print
"<blockquote><pre>"
3524 getcomments html
$P $PP
3527 # Add additional comments comment
3528 print
"<!-- Add comments to explain changes in $P here -->"
3530 # Add count of changes.
3531 if [[ -f $F.count
]]; then
3536 if [[ $SCM_MODE == "unknown" ]]; then
3537 # Include warnings for important file mode situations:
3538 # 1) New executable files
3539 # 2) Permission changes of any kind
3540 # 3) Existing executable files
3542 if [[ -f $WDIR/raw_files
/old
/$PP ]]; then
3543 old_mode
=`get_file_mode $WDIR/raw_files/old/$PP`
3547 if [[ -f $WDIR/raw_files
/new
/$P ]]; then
3548 new_mode
=`get_file_mode $WDIR/raw_files/new/$P`
3551 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3552 print
"<span class=\"chmod\">"
3553 print
"<p>new executable file: mode $new_mode</p>"
3555 elif [[ -n "$old_mode" && -n "$new_mode" &&
3556 "$old_mode" != "$new_mode" ]]; then
3557 print
"<span class=\"chmod\">"
3558 print
"<p>mode change: $old_mode to $new_mode</p>"
3560 elif [[ "$new_mode" = *[1357]* ]]; then
3561 print
"<span class=\"chmod\">"
3562 print
"<p>executable file: mode $new_mode</p>"
3567 print
"</blockquote>"
3573 print
"<p style=\"font-size: small\">"
3574 print
"This code review page was prepared using <b>$0</b>."
3575 print
"Webrev is maintained by the <a href=\"http://www.illumos.org\">"
3576 print
"illumos</a> project. The latest version may be obtained"
3577 print
"<a href=\"http://src.illumos.org/source/xref/illumos-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>"
3581 exec 1<&- # Close FD 1.
3582 exec 1<&3 # dup FD 3 to restore stdout.
3583 exec 3<&- # close FD 3.
3588 # If remote deletion was specified and fails do not continue.
3590 if [[ -n $Dflag ]]; then
3592 (( $?
== 0 )) ||
exit $?
3595 if [[ -n $Uflag ]]; then