9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22
23 #
24 # Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25 #
26
27 # Copyright 2008, 2010, Richard Lowe
28 # Copyright 2012 Marcel Telka <marcel@telka.sk>
29
30 #
31 # This script takes a file list and a workspace and builds a set of html files
32 # suitable for doing a code review of source changes via a web page.
33 # Documentation is available via the manual page, webrev.1, or just
34 # type 'webrev -h'.
35 #
36 # Acknowledgements to contributors to webrev are listed in the webrev(1)
37 # man page.
38 #
39
40 REMOVED_COLOR=brown
41 CHANGED_COLOR=blue
42 NEW_COLOR=blue
43
44 HTML='<?xml version="1.0"?>
45 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
46 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
47 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
48
1415
1416 #
1417 # source_to_html { new | old } <filename>
1418 #
1419 # Process a plain vanilla source file to transform it into an HTML file.
1420 #
1421 source_to_html()
1422 {
1423 WHICH=$1
1424 TNAME=$2
1425
1426 print "$HTML<head>$STDHEAD"
1427 print "<title>$WNAME $WHICH $TNAME</title>"
1428 print "<body id=\"SUNWwebrev\">"
1429 print "<pre>"
1430 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1431 print "</pre></body></html>"
1432 }
1433
1434 #
1435 # comments_from_teamware {text|html} parent-file child-file
1436 #
1437 # Find the first delta in the child that's not in the parent. Get the
1438 # newest delta from the parent, get all deltas from the child starting
1439 # with that delta, and then get all info starting with the second oldest
1440 # delta in that list (the first delta unique to the child).
1441 #
1442 # This code adapted from Bill Shannon's "spc" script
1443 #
1444 comments_from_teamware()
1445 {
1446 fmt=$1
1447 pfile=$PWS/$2
1448 cfile=$CWS/$3
1449
1450 if [[ ! -f $PWS/${2%/*}/SCCS/s.${2##*/} && -n $RWS ]]; then
1451 pfile=$RWS/$2
1452 fi
1453
1454 if [[ -f $pfile ]]; then
1455 psid=$($SCCS prs -d:I: $pfile 2>/dev/null)
1456 else
1457 psid=1.1
1458 fi
1459
1460 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null)
1461 N=${#sids[@]}
1462
1463 nawkprg='
1464 /^COMMENTS:/ {p=1; continue}
1465 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
1466 NF == 0u { continue }
1467 {if (p==0) continue; print $0 }'
1468
1469 if [[ $N -ge 2 ]]; then
1470 sid1=${sids[$((N-2))]} # Gets 2nd to last sid
1471
1472 if [[ $fmt == "text" ]]; then
1473 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \
1474 $AWK "$nawkprg"
1475 return
1476 fi
1477
1478 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \
1479 html_quote | its2url | $AWK "$nawkprg"
1480 fi
1481 }
1482
1483 #
1484 # comments_from_wx {text|html} filepath
1485 #
1486 # Given the pathname of a file, find its location in a "wx" active
1487 # file list and print the following comment. Output is either text or
1488 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1489 # are turned into URLs.
1490 #
1491 # This is also used with Mercurial and the file list provided by hg-active.
1492 #
1493 comments_from_wx()
1494 {
1495 typeset fmt=$1
1496 typeset p=$2
1497
1498 comm=`$AWK '
1499 $1 == "'$p'" {
1500 do getline ; while (NF > 0)
1501 getline
1502 while (NF > 0) { print ; getline }
1503 exit
1519 #
1520 # getcomments {text|html} filepath parentpath
1521 #
1522 # Fetch the comments depending on what SCM mode we're in.
1523 #
1524 getcomments()
1525 {
1526 typeset fmt=$1
1527 typeset p=$2
1528 typeset pp=$3
1529
1530 if [[ -n $Nflag ]]; then
1531 return
1532 fi
1533 #
1534 # Mercurial support uses a file list in wx format, so this
1535 # will be used there, too
1536 #
1537 if [[ -n $wxfile ]]; then
1538 comments_from_wx $fmt $p
1539 else
1540 if [[ $SCM_MODE == "teamware" ]]; then
1541 comments_from_teamware $fmt $pp $p
1542 fi
1543 fi
1544 }
1545
1546 #
1547 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1548 #
1549 # Print out Code Inspection figures similar to sccs-prt(1) format.
1550 #
1551 function printCI
1552 {
1553 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1554 typeset str
1555 if (( tot == 1 )); then
1556 str="line"
1557 else
1558 str="lines"
1559 fi
1560 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \
1561 $tot $str $ins $del $mod $unc
1562 }
1701 #
1702 # If the wx file pathname is relative then make it absolute
1703 # because the webrev does a "cd" later on.
1704 #
1705 wxfile=$PWD/$argfile
1706 else
1707 wxfile=$argfile
1708 fi
1709
1710 $AWK '{ c = 1; print;
1711 while (getline) {
1712 if (NF == 0) { c = -c; continue }
1713 if (c > 0) print
1714 }
1715 }' $wxfile > $FLIST
1716
1717 print " Done."
1718 }
1719
1720 #
1721 # flist_from_teamware [ <args-to-putback-n> ]
1722 #
1723 # Generate the file list by extracting file names from a putback -n. Some
1724 # names may come from the "update/create" messages and others from the
1725 # "currently checked out" warning. Renames are detected here too. Extract
1726 # values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
1727 # -n as well, but remove them if they are already defined.
1728 #
1729 function flist_from_teamware
1730 {
1731 if [[ -n $codemgr_parent && -z $parent_webrev ]]; then
1732 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then
1733 print -u2 "parent $codemgr_parent doesn't look like a" \
1734 "valid teamware workspace"
1735 exit 1
1736 fi
1737 parent_args="-p $codemgr_parent"
1738 fi
1739
1740 print " File list from: 'putback -n $parent_args $*' ... \c"
1741
1742 putback -n $parent_args $* 2>&1 |
1743 $AWK '
1744 /^update:|^create:/ {print $2}
1745 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)}
1746 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)}
1747 /^The following files are currently checked out/ {p = 1; continue}
1748 NF == 0 {p=0 ; continue}
1749 /^rename/ {old=$3}
1750 $1 == "to:" {print $2, old}
1751 /^"/ {continue}
1752 p == 1 {print $1}' |
1753 sort -r -k 1,1 -u | sort > $FLIST
1754
1755 print " Done."
1756 }
1757
1758 #
1759 # Call hg-active to get the active list output in the wx active list format
1760 #
1761 function hg_active_wxfile
1762 {
1763 typeset child=$1
1764 typeset parent=$2
1765
1766 TMPFLIST=/tmp/$$.active
1767 $HG_ACTIVE -w $child -p $parent -o $TMPFLIST
1768 wxfile=$TMPFLIST
1769 }
1770
1771 #
1772 # flist_from_mercurial
1773 # Call hg-active to get a wx-style active list, and hand it off to
1774 # flist_from_wx
1775 #
1776 function flist_from_mercurial
1777 {
1778 typeset child=$1
1899 export CODEMGR_WS
1900 fi
1901
1902 #
1903 # Check to see if CODEMGR_PARENT is set in the flist file.
1904 #
1905 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
1906 codemgr_parent=$CODEMGR_PARENT
1907 export CODEMGR_PARENT
1908 fi
1909 }
1910
1911 function look_for_prog
1912 {
1913 typeset path
1914 typeset ppath
1915 typeset progname=$1
1916
1917 ppath=$PATH
1918 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1919 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
1920 ppath=$ppath:/opt/onbld/bin/`uname -p`
1921
1922 PATH=$ppath prog=`whence $progname`
1923 if [[ -n $prog ]]; then
1924 print $prog
1925 fi
1926 }
1927
1928 function get_file_mode
1929 {
1930 $PERL -e '
1931 if (@stat = stat($ARGV[0])) {
1932 $mode = $stat[2] & 0777;
1933 printf "%03o\n", $mode;
1934 exit 0;
1935 } else {
1936 exit 1;
1937 }
1938 ' $1
1939 }
1940
1941 function build_old_new_teamware
1942 {
1943 typeset olddir="$1"
1944 typeset newdir="$2"
1945
1946 # If the child's version doesn't exist then
1947 # get a readonly copy.
1948
1949 if [[ ! -f $CWS/$DIR/$F && -f $CWS/$DIR/SCCS/s.$F ]]; then
1950 $SCCS get -s -p $CWS/$DIR/$F > $CWS/$DIR/$F
1951 fi
1952
1953 # The following two sections propagate file permissions the
1954 # same way SCCS does. If the file is already under version
1955 # control, always use permissions from the SCCS/s.file. If
1956 # the file is not under SCCS control, use permissions from the
1957 # working copy. In all cases, the file copied to the webrev
1958 # is set to read only, and group/other permissions are set to
1959 # match those of the file owner. This way, even if the file
1960 # is currently checked out, the webrev will display the final
1961 # permissions that would result after check in.
1962
1963 #
1964 # Snag new version of file.
1965 #
1966 rm -f $newdir/$DIR/$F
1967 cp $CWS/$DIR/$F $newdir/$DIR/$F
1968 if [[ -f $CWS/$DIR/SCCS/s.$F ]]; then
1969 chmod `get_file_mode $CWS/$DIR/SCCS/s.$F` \
1970 $newdir/$DIR/$F
1971 fi
1972 chmod u-w,go=u $newdir/$DIR/$F
1973
1974 #
1975 # Get the parent's version of the file. First see whether the
1976 # child's version is checked out and get the parent's version
1977 # with keywords expanded or unexpanded as appropriate.
1978 #
1979 if [[ -f $PWS/$PDIR/$PF && ! -f $PWS/$PDIR/SCCS/s.$PF && \
1980 ! -f $PWS/$PDIR/SCCS/p.$PF ]]; then
1981 # Parent is not a real workspace, but just a raw
1982 # directory tree - use the file that's there as
1983 # the old file.
1984
1985 rm -f $olddir/$PDIR/$PF
1986 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
1987 else
1988 if [[ -f $PWS/$PDIR/SCCS/s.$PF ]]; then
1989 real_parent=$PWS
1990 else
1991 real_parent=$RWS
1992 fi
1993
1994 rm -f $olddir/$PDIR/$PF
1995
1996 if [[ -f $real_parent/$PDIR/$PF ]]; then
1997 if [ -f $CWS/$DIR/SCCS/p.$F ]; then
1998 $SCCS get -s -p -k $real_parent/$PDIR/$PF > \
1999 $olddir/$PDIR/$PF
2000 else
2001 $SCCS get -s -p $real_parent/$PDIR/$PF > \
2002 $olddir/$PDIR/$PF
2003 fi
2004 chmod `get_file_mode $real_parent/$PDIR/SCCS/s.$PF` \
2005 $olddir/$PDIR/$PF
2006 fi
2007 fi
2008 if [[ -f $olddir/$PDIR/$PF ]]; then
2009 chmod u-w,go=u $olddir/$PDIR/$PF
2010 fi
2011 }
2012
2013 function build_old_new_mercurial
2014 {
2015 typeset olddir="$1"
2016 typeset newdir="$2"
2017 typeset old_mode=
2018 typeset new_mode=
2019 typeset file
2020
2021 #
2022 # Get old file mode, from the parent revision manifest entry.
2023 # Mercurial only stores a "file is executable" flag, but the
2024 # manifest will display an octal mode "644" or "755".
2025 #
2026 if [[ "$PDIR" == "." ]]; then
2027 file="$PF"
2028 else
2029 file="$PDIR/$PF"
2030 fi
2031 file=`echo $file | $SED 's#/#\\\/#g'`
2032 # match the exact filename, and return only the permission digits
2183 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2184 fi
2185 }
2186
2187 function build_old_new
2188 {
2189 typeset WDIR=$1
2190 typeset PWS=$2
2191 typeset PDIR=$3
2192 typeset PF=$4
2193 typeset CWS=$5
2194 typeset DIR=$6
2195 typeset F=$7
2196
2197 typeset olddir="$WDIR/raw_files/old"
2198 typeset newdir="$WDIR/raw_files/new"
2199
2200 mkdir -p $olddir/$PDIR
2201 mkdir -p $newdir/$DIR
2202
2203 if [[ $SCM_MODE == "teamware" ]]; then
2204 build_old_new_teamware "$olddir" "$newdir"
2205 elif [[ $SCM_MODE == "mercurial" ]]; then
2206 build_old_new_mercurial "$olddir" "$newdir"
2207 elif [[ $SCM_MODE == "git" ]]; then
2208 build_old_new_git "$olddir" "$newdir"
2209 elif [[ $SCM_MODE == "subversion" ]]; then
2210 build_old_new_subversion "$olddir" "$newdir"
2211 elif [[ $SCM_MODE == "unknown" ]]; then
2212 build_old_new_unknown "$olddir" "$newdir"
2213 fi
2214
2215 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2216 print "*** Error: file not in parent or child"
2217 return 1
2218 fi
2219 return 0
2220 }
2221
2222
2223 #
2224 # Usage message.
2225 #
2229 webrev [common-options] ( <file> | - )
2230 webrev [common-options] -w <wx file>
2231
2232 Options:
2233 -C <filename>: Use <filename> for the information tracking configuration.
2234 -D: delete remote webrev
2235 -i <filename>: Include <filename> in the index.html file.
2236 -I <filename>: Use <filename> for the information tracking registry.
2237 -n: do not generate the webrev (useful with -U)
2238 -O: Print bugids/arc cases suitable for OpenSolaris.
2239 -o <outdir>: Output webrev to specified directory.
2240 -p <compare-against>: Use specified parent wkspc or basis for comparison
2241 -t <remote_target>: Specify remote destination for webrev upload
2242 -U: upload the webrev to remote destination
2243 -w <wxfile>: Use specified wx active file.
2244
2245 Environment:
2246 WDIR: Control the output directory.
2247 WEBREV_TRASH_DIR: Set directory for webrev delete.
2248
2249 SCM Specific Options:
2250 TeamWare: webrev [common-options] -l [arguments to 'putback']
2251
2252 SCM Environment:
2253 CODEMGR_WS: Workspace location.
2254 CODEMGR_PARENT: Parent workspace location.
2255 '
2256
2257 exit 2
2258 }
2259
2260 #
2261 #
2262 # Main program starts here
2263 #
2264 #
2265
2266 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2267
2268 set +o noclobber
2269
2270 PATH=$(/bin/dirname "$(whence $0)"):$PATH
2271
2398
2399 # more sanity checking
2400 if [[ -n $nflag && -z $Uflag ]]; then
2401 print "it does not make sense to skip webrev generation" \
2402 "without -U"
2403 exit 1
2404 fi
2405
2406 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2407 echo "remote target has to be used only for upload or delete"
2408 exit 1
2409 fi
2410
2411 #
2412 # For the invocation "webrev -n -U" with no other options, webrev will assume
2413 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2414 # $(basename ${CWS}). So we need to get CWS set before we skip any remaining
2415 # logic.
2416 #
2417 $WHICH_SCM | read SCM_MODE junk || exit 1
2418 if [[ $SCM_MODE == "teamware" ]]; then
2419 #
2420 # Teamware priorities:
2421 # 1. CODEMGR_WS from the environment
2422 # 2. workspace name
2423 #
2424 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
2425 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
2426 print -u2 "$codemgr_ws: no such workspace"
2427 exit 1
2428 fi
2429 [[ -z $codemgr_ws ]] && codemgr_ws=$(workspace name)
2430 codemgr_ws=$(cd $codemgr_ws;print $PWD)
2431 CODEMGR_WS=$codemgr_ws
2432 CWS=$codemgr_ws
2433 elif [[ $SCM_MODE == "mercurial" ]]; then
2434 #
2435 # Mercurial priorities:
2436 # 1. hg root from CODEMGR_WS environment variable
2437 # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under
2438 # usr/closed when we run webrev
2439 # 2. hg root from directory of invocation
2440 #
2441 if [[ ${PWD} =~ "usr/closed" ]]; then
2442 testparent=${CODEMGR_WS}/usr/closed
2443 # If we're in OpenSolaris mode, we enforce a minor policy:
2444 # help to make sure the reviewer doesn't accidentally publish
2445 # source which is under usr/closed
2446 if [[ -n "$Oflag" ]]; then
2447 print -u2 "OpenSolaris output not permitted with" \
2448 "usr/closed changes"
2449 exit 1
2450 fi
2451 else
2452 testparent=${CODEMGR_WS}
2453 fi
2539 elif [[ -n $1 ]]; then
2540 if [[ ! -r $1 ]]; then
2541 print -u2 "$1: no such file or not readable"
2542 usage
2543 fi
2544 cat $1 > $FLIST
2545 flist_mode="file"
2546 flist_file=$1
2547 flist_done=1
2548 shift
2549 else
2550 flist_mode="auto"
2551 fi
2552 fi
2553
2554 #
2555 # Before we go on to further consider -l and -w, work out which SCM we think
2556 # is in use.
2557 #
2558 case "$SCM_MODE" in
2559 teamware|mercurial|git|subversion)
2560 ;;
2561 unknown)
2562 if [[ $flist_mode == "auto" ]]; then
2563 print -u2 "Unable to determine SCM in use and file list not specified"
2564 print -u2 "See which_scm(1) for SCM detection information."
2565 exit 1
2566 fi
2567 ;;
2568 *)
2569 if [[ $flist_mode == "auto" ]]; then
2570 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2571 exit 1
2572 fi
2573 ;;
2574 esac
2575
2576 print -u2 " SCM detected: $SCM_MODE"
2577
2578 if [[ -n $lflag ]]; then
2579 #
2580 # If the -l flag is given instead of the name of a file list,
2581 # then generate the file list by extracting file names from a
2582 # putback -n.
2583 #
2584 shift $(($OPTIND - 1))
2585 if [[ $SCM_MODE == "teamware" ]]; then
2586 flist_from_teamware "$*"
2587 else
2588 print -u2 -- "Error: -l option only applies to TeamWare"
2589 exit 1
2590 fi
2591 flist_done=1
2592 shift $#
2593 elif [[ -n $wflag ]]; then
2594 #
2595 # If the -w is given then assume the file list is in Bonwick's "wx"
2596 # command format, i.e. pathname lines alternating with SCCS comment
2597 # lines with blank lines as separators. Use the SCCS comments later
2598 # in building the index.html file.
2599 #
2600 shift $(($OPTIND - 1))
2601 wxfile=$1
2602 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2603 if [[ -r $CODEMGR_WS/wx/active ]]; then
2604 wxfile=$CODEMGR_WS/wx/active
2605 fi
2606 fi
2607
2608 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
2609 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2610
2611 if [[ ! -r $wxfile ]]; then
2612 print -u2 "$wxfile: no such file or not readable"
2613 usage
2616 print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2617 flist_from_wx $wxfile
2618 flist_done=1
2619 if [[ -n "$*" ]]; then
2620 shift
2621 fi
2622 elif [[ $flist_mode == "stdin" ]]; then
2623 print -u2 " File list from: standard input"
2624 elif [[ $flist_mode == "file" ]]; then
2625 print -u2 " File list from: $flist_file"
2626 fi
2627
2628 if [[ $# -gt 0 ]]; then
2629 print -u2 "WARNING: unused arguments: $*"
2630 fi
2631
2632 #
2633 # Before we entered the DO_EVERYTHING loop, we should have already set CWS
2634 # and CODEMGR_WS as needed. Here, we set the parent workspace.
2635 #
2636
2637 if [[ $SCM_MODE == "teamware" ]]; then
2638
2639 #
2640 # Teamware priorities:
2641 #
2642 # 1) via -p command line option
2643 # 2) in the user environment
2644 # 3) in the flist
2645 # 4) automatically based on the workspace
2646 #
2647
2648 #
2649 # For 1, codemgr_parent will already be set. Here's 2:
2650 #
2651 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
2652 codemgr_parent=$CODEMGR_PARENT
2653 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
2654 print -u2 "$codemgr_parent: no such directory"
2655 exit 1
2656 fi
2657
2658 #
2659 # If we're in auto-detect mode and we haven't already gotten the file
2660 # list, then see if we can get it by probing for wx.
2661 #
2662 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
2663 if [[ ! -x $WX ]]; then
2664 print -u2 "WARNING: wx not found!"
2665 fi
2666
2667 #
2668 # We need to use wx list -w so that we get renamed files, etc.
2669 # but only if a wx active file exists-- otherwise wx will
2670 # hang asking us to initialize our wx information.
2671 #
2672 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
2673 print -u2 " File list from: 'wx list -w' ... \c"
2674 $WX list -w > $FLIST
2675 $WX comments > /tmp/$$.wx_comments
2676 wxfile=/tmp/$$.wx_comments
2677 print -u2 "done"
2678 flist_done=1
2679 fi
2680 fi
2681
2682 #
2683 # If by hook or by crook we've gotten a file list by now (perhaps
2684 # from the command line), eval it to extract environment variables from
2685 # it: This is method 3 for finding the parent.
2686 #
2687 if [[ -z $flist_done ]]; then
2688 flist_from_teamware
2689 fi
2690 env_from_flist
2691
2692 #
2693 # (4) If we still don't have a value for codemgr_parent, get it
2694 # from workspace.
2695 #
2696 [[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent`
2697 if [[ ! -d $codemgr_parent ]]; then
2698 print -u2 "$CODEMGR_PARENT: no such parent workspace"
2699 exit 1
2700 fi
2701
2702 PWS=$codemgr_parent
2703
2704 [[ -n $parent_webrev ]] && RWS=$(workspace parent $CWS)
2705
2706 elif [[ $SCM_MODE == "mercurial" ]]; then
2707 #
2708 # Parent can either be specified with -p
2709 # Specified with CODEMGR_PARENT in the environment
2710 # or taken from hg's default path.
2711 #
2712
2713 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2714 codemgr_parent=$CODEMGR_PARENT
2715 fi
2716
2717 if [[ -z $codemgr_parent ]]; then
2718 codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null`
2719 fi
2720
2721 PWS=$codemgr_parent
2722
2723 #
2724 # If the parent is a webrev, we want to do some things against
2725 # the natural workspace parent (file list, comments, etc)
2726 #
3666 fi
3667
3668 print "</p>"
3669 # Insert delta comments
3670
3671 print "<blockquote><pre>"
3672 getcomments html $P $PP
3673 print "</pre>"
3674
3675 # Add additional comments comment
3676
3677 print "<!-- Add comments to explain changes in $P here -->"
3678
3679 # Add count of changes.
3680
3681 if [[ -f $F.count ]]; then
3682 cat $F.count
3683 rm $F.count
3684 fi
3685
3686 if [[ $SCM_MODE == "teamware" ||
3687 $SCM_MODE == "mercurial" ||
3688 $SCM_MODE == "unknown" ]]; then
3689
3690 # Include warnings for important file mode situations:
3691 # 1) New executable files
3692 # 2) Permission changes of any kind
3693 # 3) Existing executable files
3694
3695 old_mode=
3696 if [[ -f $WDIR/raw_files/old/$PP ]]; then
3697 old_mode=`get_file_mode $WDIR/raw_files/old/$PP`
3698 fi
3699
3700 new_mode=
3701 if [[ -f $WDIR/raw_files/new/$P ]]; then
3702 new_mode=`get_file_mode $WDIR/raw_files/new/$P`
3703 fi
3704
3705 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3706 print "<span class=\"chmod\">"
3707 print "<p>new executable file: mode $new_mode</p>"
|
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22
23 #
24 # Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25 #
26
27 # Copyright 2008, 2010, Richard Lowe
28 # Copyright 2012 Marcel Telka <marcel@telka.sk>
29 # Copyright 2014 Bart Coddens <bart.coddens@gmail.com>
30
31 #
32 # This script takes a file list and a workspace and builds a set of html files
33 # suitable for doing a code review of source changes via a web page.
34 # Documentation is available via the manual page, webrev.1, or just
35 # type 'webrev -h'.
36 #
37 # Acknowledgements to contributors to webrev are listed in the webrev(1)
38 # man page.
39 #
40
41 REMOVED_COLOR=brown
42 CHANGED_COLOR=blue
43 NEW_COLOR=blue
44
45 HTML='<?xml version="1.0"?>
46 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
47 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
48 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
49
1416
1417 #
1418 # source_to_html { new | old } <filename>
1419 #
1420 # Process a plain vanilla source file to transform it into an HTML file.
1421 #
1422 source_to_html()
1423 {
1424 WHICH=$1
1425 TNAME=$2
1426
1427 print "$HTML<head>$STDHEAD"
1428 print "<title>$WNAME $WHICH $TNAME</title>"
1429 print "<body id=\"SUNWwebrev\">"
1430 print "<pre>"
1431 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1432 print "</pre></body></html>"
1433 }
1434
1435 #
1436 # comments_from_wx {text|html} filepath
1437 #
1438 # Given the pathname of a file, find its location in a "wx" active
1439 # file list and print the following comment. Output is either text or
1440 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1441 # are turned into URLs.
1442 #
1443 # This is also used with Mercurial and the file list provided by hg-active.
1444 #
1445 comments_from_wx()
1446 {
1447 typeset fmt=$1
1448 typeset p=$2
1449
1450 comm=`$AWK '
1451 $1 == "'$p'" {
1452 do getline ; while (NF > 0)
1453 getline
1454 while (NF > 0) { print ; getline }
1455 exit
1471 #
1472 # getcomments {text|html} filepath parentpath
1473 #
1474 # Fetch the comments depending on what SCM mode we're in.
1475 #
1476 getcomments()
1477 {
1478 typeset fmt=$1
1479 typeset p=$2
1480 typeset pp=$3
1481
1482 if [[ -n $Nflag ]]; then
1483 return
1484 fi
1485 #
1486 # Mercurial support uses a file list in wx format, so this
1487 # will be used there, too
1488 #
1489 if [[ -n $wxfile ]]; then
1490 comments_from_wx $fmt $p
1491 fi
1492 }
1493
1494 #
1495 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1496 #
1497 # Print out Code Inspection figures similar to sccs-prt(1) format.
1498 #
1499 function printCI
1500 {
1501 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1502 typeset str
1503 if (( tot == 1 )); then
1504 str="line"
1505 else
1506 str="lines"
1507 fi
1508 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \
1509 $tot $str $ins $del $mod $unc
1510 }
1649 #
1650 # If the wx file pathname is relative then make it absolute
1651 # because the webrev does a "cd" later on.
1652 #
1653 wxfile=$PWD/$argfile
1654 else
1655 wxfile=$argfile
1656 fi
1657
1658 $AWK '{ c = 1; print;
1659 while (getline) {
1660 if (NF == 0) { c = -c; continue }
1661 if (c > 0) print
1662 }
1663 }' $wxfile > $FLIST
1664
1665 print " Done."
1666 }
1667
1668 #
1669 # Call hg-active to get the active list output in the wx active list format
1670 #
1671 function hg_active_wxfile
1672 {
1673 typeset child=$1
1674 typeset parent=$2
1675
1676 TMPFLIST=/tmp/$$.active
1677 $HG_ACTIVE -w $child -p $parent -o $TMPFLIST
1678 wxfile=$TMPFLIST
1679 }
1680
1681 #
1682 # flist_from_mercurial
1683 # Call hg-active to get a wx-style active list, and hand it off to
1684 # flist_from_wx
1685 #
1686 function flist_from_mercurial
1687 {
1688 typeset child=$1
1809 export CODEMGR_WS
1810 fi
1811
1812 #
1813 # Check to see if CODEMGR_PARENT is set in the flist file.
1814 #
1815 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
1816 codemgr_parent=$CODEMGR_PARENT
1817 export CODEMGR_PARENT
1818 fi
1819 }
1820
1821 function look_for_prog
1822 {
1823 typeset path
1824 typeset ppath
1825 typeset progname=$1
1826
1827 ppath=$PATH
1828 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1829 ppath=$ppath:/opt/onbld/bin
1830 ppath=$ppath:/opt/onbld/bin/`uname -p`
1831
1832 PATH=$ppath prog=`whence $progname`
1833 if [[ -n $prog ]]; then
1834 print $prog
1835 fi
1836 }
1837
1838 function get_file_mode
1839 {
1840 $PERL -e '
1841 if (@stat = stat($ARGV[0])) {
1842 $mode = $stat[2] & 0777;
1843 printf "%03o\n", $mode;
1844 exit 0;
1845 } else {
1846 exit 1;
1847 }
1848 ' $1
1849 }
1850
1851 function build_old_new_mercurial
1852 {
1853 typeset olddir="$1"
1854 typeset newdir="$2"
1855 typeset old_mode=
1856 typeset new_mode=
1857 typeset file
1858
1859 #
1860 # Get old file mode, from the parent revision manifest entry.
1861 # Mercurial only stores a "file is executable" flag, but the
1862 # manifest will display an octal mode "644" or "755".
1863 #
1864 if [[ "$PDIR" == "." ]]; then
1865 file="$PF"
1866 else
1867 file="$PDIR/$PF"
1868 fi
1869 file=`echo $file | $SED 's#/#\\\/#g'`
1870 # match the exact filename, and return only the permission digits
2021 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2022 fi
2023 }
2024
2025 function build_old_new
2026 {
2027 typeset WDIR=$1
2028 typeset PWS=$2
2029 typeset PDIR=$3
2030 typeset PF=$4
2031 typeset CWS=$5
2032 typeset DIR=$6
2033 typeset F=$7
2034
2035 typeset olddir="$WDIR/raw_files/old"
2036 typeset newdir="$WDIR/raw_files/new"
2037
2038 mkdir -p $olddir/$PDIR
2039 mkdir -p $newdir/$DIR
2040
2041 if [[ $SCM_MODE == "mercurial" ]]; then
2042 build_old_new_mercurial "$olddir" "$newdir"
2043 elif [[ $SCM_MODE == "git" ]]; then
2044 build_old_new_git "$olddir" "$newdir"
2045 elif [[ $SCM_MODE == "subversion" ]]; then
2046 build_old_new_subversion "$olddir" "$newdir"
2047 elif [[ $SCM_MODE == "unknown" ]]; then
2048 build_old_new_unknown "$olddir" "$newdir"
2049 fi
2050
2051 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2052 print "*** Error: file not in parent or child"
2053 return 1
2054 fi
2055 return 0
2056 }
2057
2058
2059 #
2060 # Usage message.
2061 #
2065 webrev [common-options] ( <file> | - )
2066 webrev [common-options] -w <wx file>
2067
2068 Options:
2069 -C <filename>: Use <filename> for the information tracking configuration.
2070 -D: delete remote webrev
2071 -i <filename>: Include <filename> in the index.html file.
2072 -I <filename>: Use <filename> for the information tracking registry.
2073 -n: do not generate the webrev (useful with -U)
2074 -O: Print bugids/arc cases suitable for OpenSolaris.
2075 -o <outdir>: Output webrev to specified directory.
2076 -p <compare-against>: Use specified parent wkspc or basis for comparison
2077 -t <remote_target>: Specify remote destination for webrev upload
2078 -U: upload the webrev to remote destination
2079 -w <wxfile>: Use specified wx active file.
2080
2081 Environment:
2082 WDIR: Control the output directory.
2083 WEBREV_TRASH_DIR: Set directory for webrev delete.
2084
2085 SCM Environment:
2086 CODEMGR_WS: Workspace location.
2087 CODEMGR_PARENT: Parent workspace location.
2088 '
2089
2090 exit 2
2091 }
2092
2093 #
2094 #
2095 # Main program starts here
2096 #
2097 #
2098
2099 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2100
2101 set +o noclobber
2102
2103 PATH=$(/bin/dirname "$(whence $0)"):$PATH
2104
2231
2232 # more sanity checking
2233 if [[ -n $nflag && -z $Uflag ]]; then
2234 print "it does not make sense to skip webrev generation" \
2235 "without -U"
2236 exit 1
2237 fi
2238
2239 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2240 echo "remote target has to be used only for upload or delete"
2241 exit 1
2242 fi
2243
2244 #
2245 # For the invocation "webrev -n -U" with no other options, webrev will assume
2246 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2247 # $(basename ${CWS}). So we need to get CWS set before we skip any remaining
2248 # logic.
2249 #
2250 $WHICH_SCM | read SCM_MODE junk || exit 1
2251 if [[ $SCM_MODE == "mercurial" ]]; then
2252 #
2253 # Mercurial priorities:
2254 # 1. hg root from CODEMGR_WS environment variable
2255 # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under
2256 # usr/closed when we run webrev
2257 # 2. hg root from directory of invocation
2258 #
2259 if [[ ${PWD} =~ "usr/closed" ]]; then
2260 testparent=${CODEMGR_WS}/usr/closed
2261 # If we're in OpenSolaris mode, we enforce a minor policy:
2262 # help to make sure the reviewer doesn't accidentally publish
2263 # source which is under usr/closed
2264 if [[ -n "$Oflag" ]]; then
2265 print -u2 "OpenSolaris output not permitted with" \
2266 "usr/closed changes"
2267 exit 1
2268 fi
2269 else
2270 testparent=${CODEMGR_WS}
2271 fi
2357 elif [[ -n $1 ]]; then
2358 if [[ ! -r $1 ]]; then
2359 print -u2 "$1: no such file or not readable"
2360 usage
2361 fi
2362 cat $1 > $FLIST
2363 flist_mode="file"
2364 flist_file=$1
2365 flist_done=1
2366 shift
2367 else
2368 flist_mode="auto"
2369 fi
2370 fi
2371
2372 #
2373 # Before we go on to further consider -l and -w, work out which SCM we think
2374 # is in use.
2375 #
2376 case "$SCM_MODE" in
2377 mercurial|git|subversion)
2378 ;;
2379 unknown)
2380 if [[ $flist_mode == "auto" ]]; then
2381 print -u2 "Unable to determine SCM in use and file list not specified"
2382 print -u2 "See which_scm(1) for SCM detection information."
2383 exit 1
2384 fi
2385 ;;
2386 *)
2387 if [[ $flist_mode == "auto" ]]; then
2388 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2389 exit 1
2390 fi
2391 ;;
2392 esac
2393
2394 print -u2 " SCM detected: $SCM_MODE"
2395
2396 if [[ -n $wflag ]]; then
2397 #
2398 # If the -w is given then assume the file list is in Bonwick's "wx"
2399 # command format, i.e. pathname lines alternating with SCCS comment
2400 # lines with blank lines as separators. Use the SCCS comments later
2401 # in building the index.html file.
2402 #
2403 shift $(($OPTIND - 1))
2404 wxfile=$1
2405 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2406 if [[ -r $CODEMGR_WS/wx/active ]]; then
2407 wxfile=$CODEMGR_WS/wx/active
2408 fi
2409 fi
2410
2411 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
2412 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2413
2414 if [[ ! -r $wxfile ]]; then
2415 print -u2 "$wxfile: no such file or not readable"
2416 usage
2419 print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2420 flist_from_wx $wxfile
2421 flist_done=1
2422 if [[ -n "$*" ]]; then
2423 shift
2424 fi
2425 elif [[ $flist_mode == "stdin" ]]; then
2426 print -u2 " File list from: standard input"
2427 elif [[ $flist_mode == "file" ]]; then
2428 print -u2 " File list from: $flist_file"
2429 fi
2430
2431 if [[ $# -gt 0 ]]; then
2432 print -u2 "WARNING: unused arguments: $*"
2433 fi
2434
2435 #
2436 # Before we entered the DO_EVERYTHING loop, we should have already set CWS
2437 # and CODEMGR_WS as needed. Here, we set the parent workspace.
2438 #
2439 if [[ $SCM_MODE == "mercurial" ]]; then
2440 #
2441 # Parent can either be specified with -p
2442 # Specified with CODEMGR_PARENT in the environment
2443 # or taken from hg's default path.
2444 #
2445
2446 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2447 codemgr_parent=$CODEMGR_PARENT
2448 fi
2449
2450 if [[ -z $codemgr_parent ]]; then
2451 codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null`
2452 fi
2453
2454 PWS=$codemgr_parent
2455
2456 #
2457 # If the parent is a webrev, we want to do some things against
2458 # the natural workspace parent (file list, comments, etc)
2459 #
3399 fi
3400
3401 print "</p>"
3402 # Insert delta comments
3403
3404 print "<blockquote><pre>"
3405 getcomments html $P $PP
3406 print "</pre>"
3407
3408 # Add additional comments comment
3409
3410 print "<!-- Add comments to explain changes in $P here -->"
3411
3412 # Add count of changes.
3413
3414 if [[ -f $F.count ]]; then
3415 cat $F.count
3416 rm $F.count
3417 fi
3418
3419 if [[ $SCM_MODE == "mercurial" ||
3420 $SCM_MODE == "unknown" ]]; then
3421
3422 # Include warnings for important file mode situations:
3423 # 1) New executable files
3424 # 2) Permission changes of any kind
3425 # 3) Existing executable files
3426
3427 old_mode=
3428 if [[ -f $WDIR/raw_files/old/$PP ]]; then
3429 old_mode=`get_file_mode $WDIR/raw_files/old/$PP`
3430 fi
3431
3432 new_mode=
3433 if [[ -f $WDIR/raw_files/new/$P ]]; then
3434 new_mode=`get_file_mode $WDIR/raw_files/new/$P`
3435 fi
3436
3437 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3438 print "<span class=\"chmod\">"
3439 print "<p>new executable file: mode $new_mode</p>"
|