The webrev.ksh Java example source code
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
# Use is subject to license terms.
#
# This script takes a file list and a workspace and builds a set of html files
# suitable for doing a code review of source changes via a web page.
# Documentation is available via 'webrev -h'.
#
WEBREV_UPDATED=25.1-hg+openjdk.java.net
HTML='<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
FRAMEHTML='<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
STDHEAD='<meta charset="utf-8">
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
<!--
Note to customizers: the body of the webrev is IDed as SUNWwebrev
to allow easy overriding by users of webrev via the userContent.css
mechanism available in some browsers.
For example, to have all "removed" information be red instead of
brown, set a rule in your userContent.css file like:
body#SUNWwebrev span.removed { color: red ! important; }
-->
<style type="text/css" media="screen">
body {
background-color: #eeeeee;
}
hr {
border: none 0;
border-top: 1px solid #aaa;
height: 1px;
}
div.summary {
font-size: .8em;
border-bottom: 1px solid #aaa;
padding-left: 1em;
padding-right: 1em;
}
div.summary h2 {
margin-bottom: 0.3em;
}
div.summary table th {
text-align: right;
vertical-align: top;
white-space: nowrap;
}
span.lineschanged {
font-size: 0.7em;
}
span.oldmarker {
color: red;
font-size: large;
font-weight: bold;
}
span.newmarker {
color: green;
font-size: large;
font-weight: bold;
}
span.removed {
color: brown;
}
span.changed {
color: blue;
}
span.new {
color: blue;
font-weight: bold;
}
a.print { font-size: x-small; }
</style>
<style type="text/css" media="print">
pre { font-size: 0.8em; font-family: courier, monospace; }
span.removed { color: #444; font-style: italic }
span.changed { font-weight: bold; }
span.new { font-weight: bold; }
span.newmarker { font-size: 1.2em; font-weight: bold; }
span.oldmarker { font-size: 1.2em; font-weight: bold; }
a.print {display: none}
hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
</style>
'
#
# UDiffs need a slightly different CSS rule for 'new' items (we don't
# want them to be bolded as we do in cdiffs or sdiffs).
#
UDIFFCSS='
<style type="text/css" media="screen">
span.new {
color: blue;
font-weight: normal;
}
</style>
'
#
# input_cmd | html_quote | output_cmd
# or
# html_quote filename | output_cmd
#
# Make a piece of source code safe for display in an HTML <pre> block.
#
html_quote()
{
sed -e "s/&/\&/g" -e "s/&#\([x]*[0-9A-Fa-f]\{2,5\}\);/\\1;/g" -e "s/</\</g" -e "s/>/\>/g" "$@" | expand
}
#
# input_cmd | html_quote | output_cmd
# or
# html_dequote filename | output_cmd
#
# Replace HTML entities with literals
#
html_dequote()
{
sed -e "s/"/\"/g" -e "s/'/\'/g" -e "s/&/\&/g" -e "s/</<'/g" -e "s/>/>/g" "$@" | expand
}
#
# input_cmd | bug2url | output_cmd
#
# Scan for bugids and insert <a> links to the relevent bug database.
#
bug2url()
{
sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL$IDPREFIX'&\">&|g'
}
#
# strip_unchanged <infile> | output_cmd
#
# Removes chunks of sdiff documents that have not changed. This makes it
# easier for a code reviewer to find the bits that have changed.
#
# Deleted lines of text are replaced by a horizontal rule. Some
# identical lines are retained before and after the changed lines to
# provide some context. The number of these lines is controlled by the
# variable C in the $AWK script below.
#
# The script detects changed lines as any line that has a "<span class="
# string embedded (unchanged lines have no particular class and are not
# part of a <span>). Blank lines (without a sequence number) are also
# detected since they flag lines that have been inserted or deleted.
#
strip_unchanged()
{
$AWK '
BEGIN { C = c = 20 }
NF == 0 || /span class=/ {
if (c > C) {
c -= C
inx = 0
if (c > C) {
print "\n</pre>
"
inx = c % C
c = C
}
for (i = 0; i < c; i++)
print ln[(inx + i) % C]
}
c = 0;
print
next
}
{ if (c >= C) {
ln[c % C] = $0
c++;
next;
}
c++;
print
}
END { if (c > (C * 2)) print "\n</pre>
" }
' $1
}
#
# sdiff_to_html
#
# This function takes two files as arguments, obtains their diff, and
# processes the diff output to present the files as an HTML document with
# the files displayed side-by-side, differences shown in color. It also
# takes a delta comment, rendered as an HTML snippet, as the third
# argument. The function takes two files as arguments, then the name of
# file, the path, and the comment. The HTML will be delivered on stdout,
# e.g.
#
# $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
# new/usr/src/tools/scripts/webrev.sh \
# webrev.sh usr/src/tools/scripts \
# '<a href="https://bugs.openjdk.java.net/browse/JDK-1234567">
# JDK-1234567</a> my bugid' > .html
#
# framed_sdiff() is then called which creates $2.frames.html
# in the webrev tree.
#
# FYI: This function is rather unusual in its use of awk. The initial
# diff run produces conventional diff output showing changed lines mixed
# with editing codes. The changed lines are ignored - we're interested in
# the editing codes, e.g.
#
# 8c8
# 57a61
# 63c66,76
# 68,93d80
# 106d90
# 108,110d91
#
# These editing codes are parsed by the awk script and used to generate
# another awk script that generates HTML, e.g the above lines would turn
# into something like this:
#
# BEGIN { printf "<pre>\n" }
# function sp(n) {for (i=0;i<n;i++)printf "\n"}
# function wl(n) {printf "<font color=%s>%4d %s \n", n, NR, $0}
# NR==8 {wl("#7A7ADD");next}
# NR==54 {wl("#7A7ADD");sp(3);next}
# NR==56 {wl("#7A7ADD");next}
# NR==57 {wl("black");printf "\n"; next}
# : :
#
# This script is then run on the original source file to generate the
# HTML that corresponds to the source file.
#
# The two HTML files are then combined into a single piece of HTML that
# uses an HTML table construct to present the files side by side. You'll
# notice that the changes are color-coded:
#
# black - unchanged lines
# blue - changed lines
# bold blue - new lines
# brown - deleted lines
#
# Blank lines are inserted in each file to keep unchanged lines in sync
# (side-by-side). This format is familiar to users of sdiff(1) or
# Teamware's filemerge tool.
#
sdiff_to_html()
{
diff -b $1 $2 > /tmp/$$.diffs
TNAME=$3
TPATH=$4
COMMENT=$5
#
# Now we have the diffs, generate the HTML for the old file.
#
$AWK '
BEGIN {
printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
printf "function removed() "
printf "{printf \"<span class=\\\"removed\\\">%%4d %%s\\n\", NR, $0}\n"
printf "function changed() "
printf "{printf \"<span class=\\\"changed\\\">%%4d %%s\\n\", NR, $0}\n"
printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
}
/^</ {next}
/^>/ {next}
/^---/ {next}
{
split($1, a, /[cad]/) ;
if (index($1, "a")) {
if (a[1] == 0) {
n = split(a[2], r, /,/);
if (n == 1)
printf "BEGIN\t\t{sp(1)}\n"
else
printf "BEGIN\t\t{sp(%d)}\n",\
(r[2] - r[1]) + 1
next
}
printf "NR==%s\t\t{", a[1]
n = split(a[2], r, /,/);
s = r[1];
if (n == 1)
printf "bl();printf \"\\n\"; next}\n"
else {
n = r[2] - r[1]
printf "bl();sp(%d);next}\n",\
(r[2] - r[1]) + 1
}
next
}
if (index($1, "d")) {
n = split(a[1], r, /,/);
n1 = r[1]
n2 = r[2]
if (n == 1)
printf "NR==%s\t\t{removed(); next}\n" , n1
else
printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
next
}
if (index($1, "c")) {
n = split(a[1], r, /,/);
n1 = r[1]
n2 = r[2]
final = n2
d1 = 0
if (n == 1)
printf "NR==%s\t\t{changed();" , n1
else {
d1 = n2 - n1
printf "NR==%s,NR==%s\t{changed();" , n1, n2
}
m = split(a[2], r, /,/);
n1 = r[1]
n2 = r[2]
if (m > 1) {
d2 = n2 - n1
if (d2 > d1) {
if (n > 1) printf "if (NR==%d)", final
printf "sp(%d);", d2 - d1
}
}
printf "next}\n" ;
next
}
}
END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
' /tmp/$$.diffs > /tmp/$$.file1
#
# Now generate the HTML for the new file
#
$AWK '
BEGIN {
printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
printf "function new() "
printf "{printf \"<span class=\\\"new\\\">%%4d %%s\\n\", NR, $0}\n"
printf "function changed() "
printf "{printf \"<span class=\\\"changed\\\">%%4d %%s\\n\", NR, $0}\n"
printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
}
/^</ {next}
/^>/ {next}
/^---/ {next}
{
split($1, a, /[cad]/) ;
if (index($1, "d")) {
if (a[2] == 0) {
n = split(a[1], r, /,/);
if (n == 1)
printf "BEGIN\t\t{sp(1)}\n"
else
printf "BEGIN\t\t{sp(%d)}\n",\
(r[2] - r[1]) + 1
next
}
printf "NR==%s\t\t{", a[2]
n = split(a[1], r, /,/);
s = r[1];
if (n == 1)
printf "bl();printf \"\\n\"; next}\n"
else {
n = r[2] - r[1]
printf "bl();sp(%d);next}\n",\
(r[2] - r[1]) + 1
}
next
}
if (index($1, "a")) {
n = split(a[2], r, /,/);
n1 = r[1]
n2 = r[2]
if (n == 1)
printf "NR==%s\t\t{new() ; next}\n" , n1
else
printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
next
}
if (index($1, "c")) {
n = split(a[2], r, /,/);
n1 = r[1]
n2 = r[2]
final = n2
d2 = 0;
if (n == 1) {
final = n1
printf "NR==%s\t\t{changed();" , n1
} else {
d2 = n2 - n1
printf "NR==%s,NR==%s\t{changed();" , n1, n2
}
m = split(a[1], r, /,/);
n1 = r[1]
n2 = r[2]
if (m > 1) {
d1 = n2 - n1
if (d1 > d2) {
if (n > 1) printf "if (NR==%d)", final
printf "sp(%d);", d1 - d2
}
}
printf "next}\n" ;
next
}
}
END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
' /tmp/$$.diffs > /tmp/$$.file2
#
# Post-process the HTML files by running them back through $AWK
#
html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
#
# Now combine into a valid HTML file and side-by-side into a table
#
print "$HTML<head>$STDHEAD"
print "<title>$WNAME Sdiff $TPATH "
print "</head>"
print "<h2>$TPATH/$TNAME"
print "<a class=\"print\" href=\"javascript:print()\">Print this page"
print "<pre>$COMMENT
\n"
print "<table>"
print "<td>"
strip_unchanged /tmp/$$.file1.html
print "</pre>"
strip_unchanged /tmp/$$.file2.html
print "</pre> | "
print "</tr>