Print this page
9001 cdm is useless, remove it
9002 webrev should know how to get the git user name
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Joshua M. Clulow <jmc@joyent.com>


1552 {
1553         WHICH=$1
1554         TNAME=$2
1555 
1556         print "$HTML<head>$STDHEAD"
1557         print "<title>$WNAME $WHICH $TNAME</title>"
1558         print "<body id=\"SUNWwebrev\">"
1559         print "<pre>"
1560         html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1561         print "</pre></body></html>"
1562 }
1563 
1564 #
1565 # comments_from_wx {text|html} filepath
1566 #
1567 # Given the pathname of a file, find its location in a "wx" active
1568 # file list and print the following comment.  Output is either text or
1569 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1570 # are turned into URLs.
1571 #
1572 # This is also used with Mercurial and the file list provided by hg-active.
1573 #
1574 comments_from_wx()
1575 {
1576         typeset fmt=$1
1577         typeset p=$2
1578 
1579         comm=`$AWK '
1580         $1 == "'$p'" {
1581                 do getline ; while (NF > 0)
1582                 getline
1583                 while (NF > 0) { print ; getline }
1584                 exit
1585         }' < $wxfile`
1586 
1587         if [[ -z $comm ]]; then
1588                 comm="*** NO COMMENTS ***"
1589         fi
1590 
1591         if [[ $fmt == "text" ]]; then
1592                 print -- "$comm"
1593                 return
1594         fi
1595 
1596         print -- "$comm" | html_quote | its2url
1597 
1598 }
1599 
1600 #
1601 # getcomments {text|html} filepath parentpath
1602 #
1603 # Fetch the comments depending on what SCM mode we're in.
1604 #
1605 getcomments()
1606 {
1607         typeset fmt=$1
1608         typeset p=$2
1609         typeset pp=$3
1610 
1611         if [[ -n $Nflag ]]; then
1612                 return
1613         fi
1614         #
1615         # Mercurial support uses a file list in wx format, so this
1616         # will be used there, too
1617         #
1618         if [[ -n $wxfile ]]; then
1619                 comments_from_wx $fmt $p
1620         fi
1621 }
1622 
1623 #
1624 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1625 #
1626 # Print out Code Inspection figures similar to sccs-prt(1) format.
1627 #
1628 function printCI
1629 {
1630         integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1631         typeset str
1632         if (( tot == 1 )); then
1633                 str="line"
1634         else
1635                 str="lines"
1636         fi
1637         printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \


1778                 #
1779                 # If the wx file pathname is relative then make it absolute
1780                 # because the webrev does a "cd" later on.
1781                 #
1782                 wxfile=$PWD/$argfile
1783         else
1784                 wxfile=$argfile
1785         fi
1786 
1787         $AWK '{ c = 1; print;
1788           while (getline) {
1789                 if (NF == 0) { c = -c; continue }
1790                 if (c > 0) print
1791           }
1792         }' $wxfile > $FLIST
1793 
1794         print " Done."
1795 }
1796 
1797 #
1798 # Call hg-active to get the active list output in the wx active list format
1799 #
1800 function hg_active_wxfile
1801 {
1802         typeset child=$1
1803         typeset parent=$2
1804 
1805         TMPFLIST=/tmp/$$.active
1806         $HG_ACTIVE -w $child -p $parent -o $TMPFLIST
1807         wxfile=$TMPFLIST
1808 }
1809 
1810 #
1811 # flist_from_mercurial
1812 # Call hg-active to get a wx-style active list, and hand it off to
1813 # flist_from_wx
1814 #
1815 function flist_from_mercurial
1816 {
1817         typeset child=$1
1818         typeset parent=$2
1819 
1820         print " File list from: hg-active -p $parent ...\c"
1821         if [[ ! -x $HG_ACTIVE ]]; then
1822                 print           # Blank line for the \c above
1823                 print -u2 "Error: hg-active tool not found.  Exiting"
1824                 exit 1
1825         fi
1826         hg_active_wxfile $child $parent
1827 
1828         # flist_from_wx prints the Done, so we don't have to.
1829         flist_from_wx $TMPFLIST
1830 }
1831 
1832 #
1833 # Transform a specified 'git log' output format into a wx-like active list.
1834 #
1835 function git_wxfile
1836 {
1837         typeset child="$1"
1838         typeset parent="$2"
1839 
1840         TMPFLIST=/tmp/$$.active
1841         $PERL -e 'my (%files, %realfiles, $msg);
1842         my $parent = $ARGV[0];
1843         my $child = $ARGV[1];
1844 
1845         open(F, "git diff -M --name-status $parent..$child |");
1846         while (<F>) {
1847             chomp;
1848             if (/^R(\d+)\s+([^ ]+)\s+([^ ]+)/) { # rename
1849                 if ($1 >= 75) {               # Probably worth treating as a rename
1850                     $realfiles{$3} = $2;
1851                 } else {
1852                     $realfiles{$3} = $3;


1962 
1963         PATH=$ppath prog=`whence $progname`
1964         if [[ -n $prog ]]; then
1965                 print $prog
1966         fi
1967 }
1968 
1969 function get_file_mode
1970 {
1971         $PERL -e '
1972                 if (@stat = stat($ARGV[0])) {
1973                         $mode = $stat[2] & 0777;
1974                         printf "%03o\n", $mode;
1975                         exit 0;
1976                 } else {
1977                         exit 1;
1978                 }
1979             ' $1
1980 }
1981 
1982 function build_old_new_mercurial
1983 {
1984         typeset olddir="$1"
1985         typeset newdir="$2"
1986         typeset old_mode=
1987         typeset new_mode=
1988         typeset file
1989 
1990         #
1991         # Get old file mode, from the parent revision manifest entry.
1992         # Mercurial only stores a "file is executable" flag, but the
1993         # manifest will display an octal mode "644" or "755".
1994         #
1995         if [[ "$PDIR" == "." ]]; then
1996                 file="$PF"
1997         else
1998                 file="$PDIR/$PF"
1999         fi
2000         file=`echo $file | $SED 's#/#\\\/#g'`
2001         # match the exact filename, and return only the permission digits
2002         old_mode=`$SED -n -e "/^\\(...\\) . ${file}$/s//\\1/p" \
2003             < $HG_PARENT_MANIFEST`
2004 
2005         #
2006         # Get new file mode, directly from the filesystem.
2007         # Normalize the mode to match Mercurial's behavior.
2008         #
2009         new_mode=`get_file_mode $CWS/$DIR/$F`
2010         if [[ -n "$new_mode" ]]; then
2011                 if [[ "$new_mode" = *[1357]* ]]; then
2012                         new_mode=755
2013                 else
2014                         new_mode=644
2015                 fi
2016         fi
2017 
2018         #
2019         # new version of the file.
2020         #
2021         rm -rf $newdir/$DIR/$F
2022         if [[ -e $CWS/$DIR/$F ]]; then
2023                 cp $CWS/$DIR/$F $newdir/$DIR/$F
2024                 if [[ -n $new_mode ]]; then
2025                         chmod $new_mode $newdir/$DIR/$F
2026                 else
2027                         # should never happen
2028                         print -u2 "ERROR: set mode of $newdir/$DIR/$F"
2029                 fi
2030         fi
2031 
2032         #
2033         # parent's version of the file
2034         #
2035         # Note that we get this from the last version common to both
2036         # ourselves and the parent.  References are via $CWS since we have no
2037         # guarantee that the parent workspace is reachable via the filesystem.
2038         #
2039         if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then
2040                 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2041         elif [[ -n $HG_PARENT ]]; then
2042                 hg cat -R $CWS -r $HG_PARENT $CWS/$PDIR/$PF > \
2043                     $olddir/$PDIR/$PF 2>/dev/null
2044 
2045                 if (( $? != 0 )); then
2046                         rm -f $olddir/$PDIR/$PF
2047                 else
2048                         if [[ -n $old_mode ]]; then
2049                                 chmod $old_mode $olddir/$PDIR/$PF
2050                         else
2051                                 # should never happen
2052                                 print -u2 "ERROR: set mode of $olddir/$PDIR/$PF"
2053                         fi
2054                 fi
2055         fi
2056 }
2057 
2058 function build_old_new_git
2059 {
2060         typeset olddir="$1"
2061         typeset newdir="$2"
2062         typeset o_mode=
2063         typeset n_mode=
2064         typeset o_object=
2065         typeset n_object=
2066         typeset OWD=$PWD
2067         typeset file
2068         typeset type
2069 
2070         cd $CWS
2071 
2072         #
2073         # Get old file and its mode from the git object tree
2074         #
2075         if [[ "$PDIR" == "." ]]; then
2076                 file="$PF"
2077         else


2152                 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2153         fi
2154 }
2155 
2156 function build_old_new
2157 {
2158         typeset WDIR=$1
2159         typeset PWS=$2
2160         typeset PDIR=$3
2161         typeset PF=$4
2162         typeset CWS=$5
2163         typeset DIR=$6
2164         typeset F=$7
2165 
2166         typeset olddir="$WDIR/raw_files/old"
2167         typeset newdir="$WDIR/raw_files/new"
2168 
2169         mkdir -p $olddir/$PDIR
2170         mkdir -p $newdir/$DIR
2171 
2172         if [[ $SCM_MODE == "mercurial" ]]; then
2173                 build_old_new_mercurial "$olddir" "$newdir"
2174         elif [[ $SCM_MODE == "git" ]]; then
2175                 build_old_new_git "$olddir" "$newdir"
2176         elif [[ $SCM_MODE == "subversion" ]]; then
2177                 build_old_new_subversion "$olddir" "$newdir"
2178         elif [[ $SCM_MODE == "unknown" ]]; then
2179                 build_old_new_unknown "$olddir" "$newdir"
2180         fi
2181 
2182         if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2183                 print "*** Error: file not in parent or child"
2184                 return 1
2185         fi
2186         return 0
2187 }
2188 
2189 
2190 #
2191 # Usage message.
2192 #
2193 function usage
2194 {


2220         CODEMGR_PARENT: Parent workspace location.
2221 '
2222 
2223         exit 2
2224 }
2225 
2226 #
2227 #
2228 # Main program starts here
2229 #
2230 #
2231 
2232 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2233 
2234 set +o noclobber
2235 
2236 PATH=$(/bin/dirname "$(whence $0)"):$PATH
2237 
2238 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
2239 [[ -z $WX ]] && WX=`look_for_prog wx`
2240 [[ -z $HG_ACTIVE ]] && HG_ACTIVE=`look_for_prog hg-active`
2241 [[ -z $GIT ]] && GIT=`look_for_prog git`
2242 [[ -z $WHICH_SCM ]] && WHICH_SCM=`look_for_prog which_scm`
2243 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
2244 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
2245 [[ -z $PERL ]] && PERL=`look_for_prog perl`
2246 [[ -z $RSYNC ]] && RSYNC=`look_for_prog rsync`
2247 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
2248 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
2249 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
2250 [[ -z $AWK ]] && AWK=`look_for_prog awk`
2251 [[ -z $SCP ]] && SCP=`look_for_prog scp`
2252 [[ -z $SED ]] && SED=`look_for_prog sed`
2253 [[ -z $SFTP ]] && SFTP=`look_for_prog sftp`
2254 [[ -z $SORT ]] && SORT=`look_for_prog sort`
2255 [[ -z $MKTEMP ]] && MKTEMP=`look_for_prog mktemp`
2256 [[ -z $GREP ]] && GREP=`look_for_prog grep`
2257 [[ -z $FIND ]] && FIND=`look_for_prog find`
2258 [[ -z $MANDOC ]] && MANDOC=`look_for_prog mandoc`
2259 [[ -z $COL ]] && COL=`look_for_prog col`
2260 


2293 cflag=
2294 Cflag=
2295 Dflag=
2296 flist_mode=
2297 flist_file=
2298 hflag=
2299 iflag=
2300 Iflag=
2301 lflag=
2302 Nflag=
2303 nflag=
2304 Oflag=
2305 oflag=
2306 pflag=
2307 tflag=
2308 uflag=
2309 Uflag=
2310 wflag=
2311 remote_target=
2312 
2313 #
2314 # NOTE: when adding/removing options it is necessary to sync the list
2315 #       with usr/src/tools/onbld/hgext/cdm.py
2316 #
2317 while getopts "c:C:Dh:i:I:lnNo:Op:t:Uw" opt
2318 do
2319         case $opt in
2320         c)      cflag=1
2321                 codemgr_head=$OPTARG
2322                 codemgr_parent=$OPTARG~1;;
2323 
2324         C)      Cflag=1
2325                 ITSCONF=$OPTARG;;
2326 
2327         D)      Dflag=1;;
2328 
2329         h)      hflag=1
2330                 codemgr_head=$OPTARG;;
2331 
2332         i)      iflag=1
2333                 INCLUDE_FILE=$OPTARG;;
2334 
2335         I)      Iflag=1
2336                 ITSREG=$OPTARG;;


2367 
2368 # more sanity checking
2369 if [[ -n $nflag && -z $Uflag ]]; then
2370         print "it does not make sense to skip webrev generation" \
2371             "without -U"
2372         exit 1
2373 fi
2374 
2375 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2376         echo "remote target has to be used only for upload or delete"
2377         exit 1
2378 fi
2379 
2380 #
2381 # For the invocation "webrev -n -U" with no other options, webrev will assume
2382 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2383 # $(basename ${CWS}).  So we need to get CWS set before we skip any remaining
2384 # logic.
2385 #
2386 $WHICH_SCM | read SCM_MODE junk || exit 1
2387 if [[ $SCM_MODE == "mercurial" ]]; then
2388         #
2389         # Mercurial priorities:
2390         # 1. hg root from CODEMGR_WS environment variable
2391         # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under
2392         #    usr/closed when we run webrev
2393         # 2. hg root from directory of invocation
2394         #
2395         if [[ ${PWD} =~ "usr/closed" ]]; then
2396                 testparent=${CODEMGR_WS}/usr/closed
2397                 # If we're in OpenSolaris mode, we enforce a minor policy:
2398                 # help to make sure the reviewer doesn't accidentally publish
2399                 # source which is under usr/closed
2400                 if [[ -n "$Oflag" ]]; then
2401                         print -u2 "OpenSolaris output not permitted with" \
2402                             "usr/closed changes"
2403                         exit 1
2404                 fi
2405         else
2406                 testparent=${CODEMGR_WS}
2407         fi
2408         [[ -z $codemgr_ws && -n $testparent ]] && \
2409             codemgr_ws=$(hg root -R $testparent 2>/dev/null)
2410         [[ -z $codemgr_ws ]] && codemgr_ws=$(hg root 2>/dev/null)
2411         CWS=$codemgr_ws
2412 elif [[ $SCM_MODE == "git" ]]; then
2413         #
2414         # Git priorities:
2415         # 1. git rev-parse --git-dir from CODEMGR_WS environment variable
2416         # 2. git rev-parse --git-dir from directory of invocation
2417         #
2418         [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && \
2419             codemgr_ws=$($GIT --git-dir=$CODEMGR_WS/.git rev-parse --git-dir \
2420                 2>/dev/null)
2421         [[ -z $codemgr_ws ]] && \
2422             codemgr_ws=$($GIT rev-parse --git-dir 2>/dev/null)
2423 
2424         if [[ "$codemgr_ws" == ".git" ]]; then
2425                 codemgr_ws="${PWD}/${codemgr_ws}"
2426         fi
2427 
2428         if [[ "$codemgr_ws" = *"/.git" ]]; then
2429                 codemgr_ws=$(dirname $codemgr_ws) # Lose the '/.git'
2430         fi
2431         CWS="$codemgr_ws"
2432 elif [[ $SCM_MODE == "subversion" ]]; then


2495         elif [[ -n $1 ]]; then
2496                 if [[ ! -r $1 ]]; then
2497                         print -u2 "$1: no such file or not readable"
2498                         usage
2499                 fi
2500                 cat $1 > $FLIST
2501                 flist_mode="file"
2502                 flist_file=$1
2503                 flist_done=1
2504                 shift
2505         else
2506                 flist_mode="auto"
2507         fi
2508 fi
2509 
2510 #
2511 # Before we go on to further consider -l and -w, work out which SCM we think
2512 # is in use.
2513 #
2514 case "$SCM_MODE" in
2515 mercurial|git|subversion)
2516         ;;
2517 unknown)
2518         if [[ $flist_mode == "auto" ]]; then
2519                 print -u2 "Unable to determine SCM in use and file list not specified"
2520                 print -u2 "See which_scm(1) for SCM detection information."
2521                 exit 1
2522         fi
2523         ;;
2524 *)
2525         if [[ $flist_mode == "auto" ]]; then
2526                 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2527                 exit 1
2528         fi
2529         ;;
2530 esac
2531 
2532 print -u2 "   SCM detected: $SCM_MODE"
2533 
2534 if [[ -n $wflag ]]; then
2535         #


2553                 print -u2 "$wxfile: no such file or not readable"
2554                 usage
2555         fi
2556 
2557         print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2558         flist_from_wx $wxfile
2559         flist_done=1
2560         if [[ -n "$*" ]]; then
2561                 shift
2562         fi
2563 elif [[ $flist_mode == "stdin" ]]; then
2564         print -u2 " File list from: standard input"
2565 elif [[ $flist_mode == "file" ]]; then
2566         print -u2 " File list from: $flist_file"
2567 fi
2568 
2569 if [[ $# -gt 0 ]]; then
2570         print -u2 "WARNING: unused arguments: $*"
2571 fi
2572 
2573 #
2574 # Before we entered the DO_EVERYTHING loop, we should have already set CWS
2575 # and CODEMGR_WS as needed.  Here, we set the parent workspace.
2576 #
2577 if [[ $SCM_MODE == "mercurial" ]]; then
2578         #
2579         # Parent can either be specified with -p
2580         # Specified with CODEMGR_PARENT in the environment
2581         # or taken from hg's default path.
2582         #
2583 
2584         if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2585                 codemgr_parent=$CODEMGR_PARENT
2586         fi
2587 
2588         if [[ -z $codemgr_parent ]]; then
2589                 codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null`
2590         fi
2591 
2592         PWS=$codemgr_parent
2593 
2594         #
2595         # If the parent is a webrev, we want to do some things against
2596         # the natural workspace parent (file list, comments, etc)
2597         #
2598         if [[ -n $parent_webrev ]]; then
2599                 real_parent=$(hg path -R $codemgr_ws default 2>/dev/null)
2600         else
2601                 real_parent=$PWS
2602         fi
2603 
2604         #
2605         # If hg-active exists, then we run it.  In the case of no explicit
2606         # flist given, we'll use it for our comments.  In the case of an
2607         # explicit flist given we'll try to use it for comments for any
2608         # files mentioned in the flist.
2609         #
2610         if [[ -z $flist_done ]]; then
2611                 flist_from_mercurial $CWS $real_parent
2612                 flist_done=1
2613         fi
2614 
2615         #
2616         # If we have a file list now, pull out any variables set
2617         # therein.  We do this now (rather than when we possibly use
2618         # hg-active to find comments) to avoid stomping specifications
2619         # in the user-specified flist.
2620         #
2621         if [[ -n $flist_done ]]; then
2622                 env_from_flist
2623         fi
2624 
2625         #
2626         # Only call hg-active if we don't have a wx formatted file already
2627         #
2628         if [[ -x $HG_ACTIVE && -z $wxfile ]]; then
2629                 print "  Comments from: hg-active -p $real_parent ...\c"
2630                 hg_active_wxfile $CWS $real_parent
2631                 print " Done."
2632         fi
2633 
2634         #
2635         # At this point we must have a wx flist either from hg-active,
2636         # or in general.  Use it to try and find our parent revision,
2637         # if we don't have one.
2638         #
2639         if [[ -z $HG_PARENT ]]; then
2640                 eval `$SED -e "s/#.*$//" $wxfile | $GREP HG_PARENT=`
2641         fi
2642 
2643         #
2644         # If we still don't have a parent, we must have been given a
2645         # wx-style active list with no HG_PARENT specification, run
2646         # hg-active and pull an HG_PARENT out of it, ignore the rest.
2647         #
2648         if [[ -z $HG_PARENT && -x $HG_ACTIVE ]]; then
2649                 $HG_ACTIVE -w $codemgr_ws -p $real_parent | \
2650                     eval `$SED -e "s/#.*$//" | $GREP HG_PARENT=`
2651         elif [[ -z $HG_PARENT ]]; then
2652                 print -u2 "Error: Cannot discover parent revision"
2653                 exit 1
2654         fi
2655 
2656         pnode=$(trim_digest $HG_PARENT)
2657         PRETTY_PWS="${PWS} (at ${pnode})"
2658         cnode=$(hg parent -R $codemgr_ws --template '{node|short}' \
2659             2>/dev/null)
2660         PRETTY_CWS="${CWS} (at ${cnode})"}
2661 elif [[ $SCM_MODE == "git" ]]; then
2662         # Check that "head" revision specified with -c or -h is sane
2663         if [[ -n $cflag || -n $hflag ]]; then
2664                 head_rev=$($GIT rev-parse --verify --quiet "$codemgr_head")
2665                 if [[ -z $head_rev ]]; then
2666                         print -u2 "Error: bad revision ${codemgr_head}"
2667                         exit 1
2668                 fi
2669         fi
2670 
2671         if [[ -z $codemgr_head ]]; then
2672                 codemgr_head="HEAD";
2673         fi
2674 
2675         # Parent can either be specified with -p, or specified with
2676         # CODEMGR_PARENT in the environment.
2677         if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2678                 codemgr_parent=$CODEMGR_PARENT
2679         fi
2680 
2681         # Try to figure out the parent based on the branch the current


3058 #
3059 # Save the file list in the webrev dir
3060 #
3061 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
3062 
3063 rm -f $WDIR/$WNAME.patch
3064 rm -f $WDIR/$WNAME.ps
3065 rm -f $WDIR/$WNAME.pdf
3066 
3067 touch $WDIR/$WNAME.patch
3068 
3069 print "   Output Files:"
3070 
3071 #
3072 # Clean up the file list: Remove comments, blank lines and env variables.
3073 #
3074 $SED -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
3075 FLIST=/tmp/$$.flist.clean
3076 
3077 #
3078 # For Mercurial, create a cache of manifest entries.
3079 #
3080 if [[ $SCM_MODE == "mercurial" ]]; then
3081         #
3082         # Transform the FLIST into a temporary sed script that matches
3083         # relevant entries in the Mercurial manifest as follows:
3084         # 1) The script will be used against the parent revision manifest,
3085         #    so for FLIST lines that have two filenames (a renamed file)
3086         #    keep only the old name.
3087         # 2) Escape all forward slashes the filename.
3088         # 3) Change the filename into another sed command that matches
3089         #    that file in "hg manifest -v" output:  start of line, three
3090         #    octal digits for file permissions, space, a file type flag
3091         #    character, space, the filename, end of line.
3092         # 4) Eliminate any duplicate entries.  (This can occur if a
3093         #    file has been used as the source of an hg cp and it's
3094         #    also been modified in the same changeset.)
3095         #
3096         SEDFILE=/tmp/$$.manifest.sed
3097         $SED '
3098                 s#^[^ ]* ##
3099                 s#/#\\\/#g
3100                 s#^.*$#/^... . &$/p#
3101         ' < $FLIST | $SORT -u > $SEDFILE
3102 
3103         #
3104         # Apply the generated script to the output of "hg manifest -v"
3105         # to get the relevant subset for this webrev.
3106         #
3107         HG_PARENT_MANIFEST=/tmp/$$.manifest
3108         hg -R $CWS manifest -v -r $HG_PARENT |
3109             $SED -n -f $SEDFILE > $HG_PARENT_MANIFEST
3110 fi
3111 
3112 #
3113 # First pass through the files: generate the per-file webrev HTML-files.
3114 #
3115 cat $FLIST | while read LINE
3116 do
3117         set - $LINE
3118         P=$1
3119 
3120         #
3121         # Normally, each line in the file list is just a pathname of a
3122         # file that has been modified or created in the child.  A file
3123         # that is renamed in the child workspace has two names on the
3124         # line: new name followed by the old name.
3125         #
3126         oldname=""
3127         oldpath=""
3128         rename=
3129         if [[ $# -eq 2 ]]; then
3130                 PP=$2                   # old filename
3131                 if [[ -f $PP ]]; then
3132                         oldname=" (copied from $PP)"


3406 print "$TOTL" > $WDIR/TotalChangedLines
3407 
3408 print "     index.html: \c"
3409 INDEXFILE=$WDIR/index.html
3410 exec 3<&1                        # duplicate stdout to FD3.
3411 exec 1<&-                        # Close stdout.
3412 exec > $INDEXFILE            # Open stdout to index file.
3413 
3414 print "$HTML<head>$STDHEAD"
3415 print "<title>$WNAME</title>"
3416 print "</head>"
3417 print "<body id=\"SUNWwebrev\">"
3418 print "<div class=\"summary\">"
3419 print "<h2>Code Review for $WNAME</h2>"
3420 
3421 print "<table>"
3422 
3423 #
3424 # Get the preparer's name:
3425 #
3426 # If the SCM detected is Mercurial, and the configuration property
3427 # ui.username is available, use that, but be careful to properly escape
3428 # angle brackets (HTML syntax characters) in the email address.
3429 #
3430 # Otherwise, use the current userid in the form "John Doe (jdoe)", but
3431 # to maintain compatibility with passwd(4), we must support '&' substitutions.
3432 #
3433 preparer=
3434 if [[ "$SCM_MODE" == mercurial ]]; then
3435         preparer=`hg showconfig ui.username 2>/dev/null`
3436         if [[ -n "$preparer" ]]; then
3437                 preparer="$(echo "$preparer" | html_quote)"
3438         fi
3439 fi
3440 if [[ -z "$preparer" ]]; then
3441         preparer=$(
3442             $PERL -e '
3443                 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<);
3444                 if ($login) {
3445                     $gcos =~ s/\&/ucfirst($login)/e;
3446                     printf "%s (%s)\n", $gcos, $login;
3447                 } else {
3448                     printf "(unknown)\n";
3449                 }
3450         ')
3451 fi
3452 
3453 PREPDATE=$(LC_ALL=C /usr/bin/date +%Y-%b-%d\ %R\ %z\ %Z)
3454 print "<tr><th>Prepared by:</th><td>$preparer on $PREPDATE</td></tr>"
3455 print "<tr><th>Workspace:</th><td>${PRETTY_CWS:-$CWS}"


3643         elif [[ -n $manpage ]]; then
3644                 print " --- ---- ---"
3645         fi
3646 
3647         print "</p>"
3648 
3649         # Insert delta comments
3650         print "<blockquote><pre>"
3651         getcomments html $P $PP
3652         print "</pre>"
3653 
3654         # Add additional comments comment
3655         print "<!-- Add comments to explain changes in $P here -->"
3656 
3657         # Add count of changes.
3658         if [[ -f $F.count ]]; then
3659             cat $F.count
3660             rm $F.count
3661         fi
3662 
3663         if [[ $SCM_MODE == "mercurial" ||
3664             $SCM_MODE == "unknown" ]]; then
3665                 # Include warnings for important file mode situations:
3666                 # 1) New executable files
3667                 # 2) Permission changes of any kind
3668                 # 3) Existing executable files
3669                 old_mode=
3670                 if [[ -f $WDIR/raw_files/old/$PP ]]; then
3671                         old_mode=`get_file_mode $WDIR/raw_files/old/$PP`
3672                 fi
3673 
3674                 new_mode=
3675                 if [[ -f $WDIR/raw_files/new/$P ]]; then
3676                         new_mode=`get_file_mode $WDIR/raw_files/new/$P`
3677                 fi
3678 
3679                 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3680                         print "<span class=\"chmod\">"
3681                         print "<p>new executable file: mode $new_mode</p>"
3682                         print "</span>"
3683                 elif [[ -n "$old_mode" && -n "$new_mode" &&
3684                     "$old_mode" != "$new_mode" ]]; then




1552 {
1553         WHICH=$1
1554         TNAME=$2
1555 
1556         print "$HTML<head>$STDHEAD"
1557         print "<title>$WNAME $WHICH $TNAME</title>"
1558         print "<body id=\"SUNWwebrev\">"
1559         print "<pre>"
1560         html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1561         print "</pre></body></html>"
1562 }
1563 
1564 #
1565 # comments_from_wx {text|html} filepath
1566 #
1567 # Given the pathname of a file, find its location in a "wx" active
1568 # file list and print the following comment.  Output is either text or
1569 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1570 # are turned into URLs.
1571 #


1572 comments_from_wx()
1573 {
1574         typeset fmt=$1
1575         typeset p=$2
1576 
1577         comm=`$AWK '
1578         $1 == "'$p'" {
1579                 do getline ; while (NF > 0)
1580                 getline
1581                 while (NF > 0) { print ; getline }
1582                 exit
1583         }' < $wxfile`
1584 
1585         if [[ -z $comm ]]; then
1586                 comm="*** NO COMMENTS ***"
1587         fi
1588 
1589         if [[ $fmt == "text" ]]; then
1590                 print -- "$comm"
1591                 return
1592         fi
1593 
1594         print -- "$comm" | html_quote | its2url
1595 
1596 }
1597 
1598 #
1599 # getcomments {text|html} filepath parentpath
1600 #
1601 # Fetch the comments depending on what SCM mode we're in.
1602 #
1603 getcomments()
1604 {
1605         typeset fmt=$1
1606         typeset p=$2
1607         typeset pp=$3
1608 
1609         if [[ -n $Nflag ]]; then
1610                 return
1611         fi
1612 



1613         if [[ -n $wxfile ]]; then
1614                 comments_from_wx $fmt $p
1615         fi
1616 }
1617 
1618 #
1619 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1620 #
1621 # Print out Code Inspection figures similar to sccs-prt(1) format.
1622 #
1623 function printCI
1624 {
1625         integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1626         typeset str
1627         if (( tot == 1 )); then
1628                 str="line"
1629         else
1630                 str="lines"
1631         fi
1632         printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \


1773                 #
1774                 # If the wx file pathname is relative then make it absolute
1775                 # because the webrev does a "cd" later on.
1776                 #
1777                 wxfile=$PWD/$argfile
1778         else
1779                 wxfile=$argfile
1780         fi
1781 
1782         $AWK '{ c = 1; print;
1783           while (getline) {
1784                 if (NF == 0) { c = -c; continue }
1785                 if (c > 0) print
1786           }
1787         }' $wxfile > $FLIST
1788 
1789         print " Done."
1790 }
1791 
1792 #



































1793 # Transform a specified 'git log' output format into a wx-like active list.
1794 #
1795 function git_wxfile
1796 {
1797         typeset child="$1"
1798         typeset parent="$2"
1799 
1800         TMPFLIST=/tmp/$$.active
1801         $PERL -e 'my (%files, %realfiles, $msg);
1802         my $parent = $ARGV[0];
1803         my $child = $ARGV[1];
1804 
1805         open(F, "git diff -M --name-status $parent..$child |");
1806         while (<F>) {
1807             chomp;
1808             if (/^R(\d+)\s+([^ ]+)\s+([^ ]+)/) { # rename
1809                 if ($1 >= 75) {               # Probably worth treating as a rename
1810                     $realfiles{$3} = $2;
1811                 } else {
1812                     $realfiles{$3} = $3;


1922 
1923         PATH=$ppath prog=`whence $progname`
1924         if [[ -n $prog ]]; then
1925                 print $prog
1926         fi
1927 }
1928 
1929 function get_file_mode
1930 {
1931         $PERL -e '
1932                 if (@stat = stat($ARGV[0])) {
1933                         $mode = $stat[2] & 0777;
1934                         printf "%03o\n", $mode;
1935                         exit 0;
1936                 } else {
1937                         exit 1;
1938                 }
1939             ' $1
1940 }
1941 












































































1942 function build_old_new_git
1943 {
1944         typeset olddir="$1"
1945         typeset newdir="$2"
1946         typeset o_mode=
1947         typeset n_mode=
1948         typeset o_object=
1949         typeset n_object=
1950         typeset OWD=$PWD
1951         typeset file
1952         typeset type
1953 
1954         cd $CWS
1955 
1956         #
1957         # Get old file and its mode from the git object tree
1958         #
1959         if [[ "$PDIR" == "." ]]; then
1960                 file="$PF"
1961         else


2036                 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2037         fi
2038 }
2039 
2040 function build_old_new
2041 {
2042         typeset WDIR=$1
2043         typeset PWS=$2
2044         typeset PDIR=$3
2045         typeset PF=$4
2046         typeset CWS=$5
2047         typeset DIR=$6
2048         typeset F=$7
2049 
2050         typeset olddir="$WDIR/raw_files/old"
2051         typeset newdir="$WDIR/raw_files/new"
2052 
2053         mkdir -p $olddir/$PDIR
2054         mkdir -p $newdir/$DIR
2055 
2056         if [[ $SCM_MODE == "git" ]]; then


2057                 build_old_new_git "$olddir" "$newdir"
2058         elif [[ $SCM_MODE == "subversion" ]]; then
2059                 build_old_new_subversion "$olddir" "$newdir"
2060         elif [[ $SCM_MODE == "unknown" ]]; then
2061                 build_old_new_unknown "$olddir" "$newdir"
2062         fi
2063 
2064         if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2065                 print "*** Error: file not in parent or child"
2066                 return 1
2067         fi
2068         return 0
2069 }
2070 
2071 
2072 #
2073 # Usage message.
2074 #
2075 function usage
2076 {


2102         CODEMGR_PARENT: Parent workspace location.
2103 '
2104 
2105         exit 2
2106 }
2107 
2108 #
2109 #
2110 # Main program starts here
2111 #
2112 #
2113 
2114 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2115 
2116 set +o noclobber
2117 
2118 PATH=$(/bin/dirname "$(whence $0)"):$PATH
2119 
2120 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
2121 [[ -z $WX ]] && WX=`look_for_prog wx`

2122 [[ -z $GIT ]] && GIT=`look_for_prog git`
2123 [[ -z $WHICH_SCM ]] && WHICH_SCM=`look_for_prog which_scm`
2124 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
2125 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
2126 [[ -z $PERL ]] && PERL=`look_for_prog perl`
2127 [[ -z $RSYNC ]] && RSYNC=`look_for_prog rsync`
2128 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
2129 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
2130 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
2131 [[ -z $AWK ]] && AWK=`look_for_prog awk`
2132 [[ -z $SCP ]] && SCP=`look_for_prog scp`
2133 [[ -z $SED ]] && SED=`look_for_prog sed`
2134 [[ -z $SFTP ]] && SFTP=`look_for_prog sftp`
2135 [[ -z $SORT ]] && SORT=`look_for_prog sort`
2136 [[ -z $MKTEMP ]] && MKTEMP=`look_for_prog mktemp`
2137 [[ -z $GREP ]] && GREP=`look_for_prog grep`
2138 [[ -z $FIND ]] && FIND=`look_for_prog find`
2139 [[ -z $MANDOC ]] && MANDOC=`look_for_prog mandoc`
2140 [[ -z $COL ]] && COL=`look_for_prog col`
2141 


2174 cflag=
2175 Cflag=
2176 Dflag=
2177 flist_mode=
2178 flist_file=
2179 hflag=
2180 iflag=
2181 Iflag=
2182 lflag=
2183 Nflag=
2184 nflag=
2185 Oflag=
2186 oflag=
2187 pflag=
2188 tflag=
2189 uflag=
2190 Uflag=
2191 wflag=
2192 remote_target=
2193 




2194 while getopts "c:C:Dh:i:I:lnNo:Op:t:Uw" opt
2195 do
2196         case $opt in
2197         c)      cflag=1
2198                 codemgr_head=$OPTARG
2199                 codemgr_parent=$OPTARG~1;;
2200 
2201         C)      Cflag=1
2202                 ITSCONF=$OPTARG;;
2203 
2204         D)      Dflag=1;;
2205 
2206         h)      hflag=1
2207                 codemgr_head=$OPTARG;;
2208 
2209         i)      iflag=1
2210                 INCLUDE_FILE=$OPTARG;;
2211 
2212         I)      Iflag=1
2213                 ITSREG=$OPTARG;;


2244 
2245 # more sanity checking
2246 if [[ -n $nflag && -z $Uflag ]]; then
2247         print "it does not make sense to skip webrev generation" \
2248             "without -U"
2249         exit 1
2250 fi
2251 
2252 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2253         echo "remote target has to be used only for upload or delete"
2254         exit 1
2255 fi
2256 
2257 #
2258 # For the invocation "webrev -n -U" with no other options, webrev will assume
2259 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2260 # $(basename ${CWS}).  So we need to get CWS set before we skip any remaining
2261 # logic.
2262 #
2263 $WHICH_SCM | read SCM_MODE junk || exit 1
2264 
2265 if [[ $SCM_MODE == "git" ]]; then
























2266         #
2267         # Git priorities:
2268         # 1. git rev-parse --git-dir from CODEMGR_WS environment variable
2269         # 2. git rev-parse --git-dir from directory of invocation
2270         #
2271         [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && \
2272             codemgr_ws=$($GIT --git-dir=$CODEMGR_WS/.git rev-parse --git-dir \
2273                 2>/dev/null)
2274         [[ -z $codemgr_ws ]] && \
2275             codemgr_ws=$($GIT rev-parse --git-dir 2>/dev/null)
2276 
2277         if [[ "$codemgr_ws" == ".git" ]]; then
2278                 codemgr_ws="${PWD}/${codemgr_ws}"
2279         fi
2280 
2281         if [[ "$codemgr_ws" = *"/.git" ]]; then
2282                 codemgr_ws=$(dirname $codemgr_ws) # Lose the '/.git'
2283         fi
2284         CWS="$codemgr_ws"
2285 elif [[ $SCM_MODE == "subversion" ]]; then


2348         elif [[ -n $1 ]]; then
2349                 if [[ ! -r $1 ]]; then
2350                         print -u2 "$1: no such file or not readable"
2351                         usage
2352                 fi
2353                 cat $1 > $FLIST
2354                 flist_mode="file"
2355                 flist_file=$1
2356                 flist_done=1
2357                 shift
2358         else
2359                 flist_mode="auto"
2360         fi
2361 fi
2362 
2363 #
2364 # Before we go on to further consider -l and -w, work out which SCM we think
2365 # is in use.
2366 #
2367 case "$SCM_MODE" in
2368 git|subversion)
2369         ;;
2370 unknown)
2371         if [[ $flist_mode == "auto" ]]; then
2372                 print -u2 "Unable to determine SCM in use and file list not specified"
2373                 print -u2 "See which_scm(1) for SCM detection information."
2374                 exit 1
2375         fi
2376         ;;
2377 *)
2378         if [[ $flist_mode == "auto" ]]; then
2379                 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2380                 exit 1
2381         fi
2382         ;;
2383 esac
2384 
2385 print -u2 "   SCM detected: $SCM_MODE"
2386 
2387 if [[ -n $wflag ]]; then
2388         #


2406                 print -u2 "$wxfile: no such file or not readable"
2407                 usage
2408         fi
2409 
2410         print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2411         flist_from_wx $wxfile
2412         flist_done=1
2413         if [[ -n "$*" ]]; then
2414                 shift
2415         fi
2416 elif [[ $flist_mode == "stdin" ]]; then
2417         print -u2 " File list from: standard input"
2418 elif [[ $flist_mode == "file" ]]; then
2419         print -u2 " File list from: $flist_file"
2420 fi
2421 
2422 if [[ $# -gt 0 ]]; then
2423         print -u2 "WARNING: unused arguments: $*"
2424 fi
2425 










2426 
2427 if [[ $SCM_MODE == "git" ]]; then













































































2428         # Check that "head" revision specified with -c or -h is sane
2429         if [[ -n $cflag || -n $hflag ]]; then
2430                 head_rev=$($GIT rev-parse --verify --quiet "$codemgr_head")
2431                 if [[ -z $head_rev ]]; then
2432                         print -u2 "Error: bad revision ${codemgr_head}"
2433                         exit 1
2434                 fi
2435         fi
2436 
2437         if [[ -z $codemgr_head ]]; then
2438                 codemgr_head="HEAD";
2439         fi
2440 
2441         # Parent can either be specified with -p, or specified with
2442         # CODEMGR_PARENT in the environment.
2443         if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2444                 codemgr_parent=$CODEMGR_PARENT
2445         fi
2446 
2447         # Try to figure out the parent based on the branch the current


2824 #
2825 # Save the file list in the webrev dir
2826 #
2827 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
2828 
2829 rm -f $WDIR/$WNAME.patch
2830 rm -f $WDIR/$WNAME.ps
2831 rm -f $WDIR/$WNAME.pdf
2832 
2833 touch $WDIR/$WNAME.patch
2834 
2835 print "   Output Files:"
2836 
2837 #
2838 # Clean up the file list: Remove comments, blank lines and env variables.
2839 #
2840 $SED -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
2841 FLIST=/tmp/$$.flist.clean
2842 
2843 #



































2844 # First pass through the files: generate the per-file webrev HTML-files.
2845 #
2846 cat $FLIST | while read LINE
2847 do
2848         set - $LINE
2849         P=$1
2850 
2851         #
2852         # Normally, each line in the file list is just a pathname of a
2853         # file that has been modified or created in the child.  A file
2854         # that is renamed in the child workspace has two names on the
2855         # line: new name followed by the old name.
2856         #
2857         oldname=""
2858         oldpath=""
2859         rename=
2860         if [[ $# -eq 2 ]]; then
2861                 PP=$2                   # old filename
2862                 if [[ -f $PP ]]; then
2863                         oldname=" (copied from $PP)"


3137 print "$TOTL" > $WDIR/TotalChangedLines
3138 
3139 print "     index.html: \c"
3140 INDEXFILE=$WDIR/index.html
3141 exec 3<&1                        # duplicate stdout to FD3.
3142 exec 1<&-                        # Close stdout.
3143 exec > $INDEXFILE            # Open stdout to index file.
3144 
3145 print "$HTML<head>$STDHEAD"
3146 print "<title>$WNAME</title>"
3147 print "</head>"
3148 print "<body id=\"SUNWwebrev\">"
3149 print "<div class=\"summary\">"
3150 print "<h2>Code Review for $WNAME</h2>"
3151 
3152 print "<table>"
3153 
3154 #
3155 # Get the preparer's name:
3156 #
3157 # If the SCM detected is Git, and the configuration property user.name is
3158 # available, use that, but be careful to properly escape angle brackets (HTML
3159 # syntax characters) in the email address.
3160 #
3161 # Otherwise, use the current userid in the form "John Doe (jdoe)", but
3162 # to maintain compatibility with passwd(4), we must support '&' substitutions.
3163 #
3164 preparer=
3165 if [[ "$SCM_MODE" == git ]]; then
3166         preparer=$(git config user.name 2>/dev/null)
3167         if [[ -n "$preparer" ]]; then
3168                 preparer="$(echo "$preparer" | html_quote)"
3169         fi
3170 fi
3171 if [[ -z "$preparer" ]]; then
3172         preparer=$(
3173             $PERL -e '
3174                 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<);
3175                 if ($login) {
3176                     $gcos =~ s/\&/ucfirst($login)/e;
3177                     printf "%s (%s)\n", $gcos, $login;
3178                 } else {
3179                     printf "(unknown)\n";
3180                 }
3181         ')
3182 fi
3183 
3184 PREPDATE=$(LC_ALL=C /usr/bin/date +%Y-%b-%d\ %R\ %z\ %Z)
3185 print "<tr><th>Prepared by:</th><td>$preparer on $PREPDATE</td></tr>"
3186 print "<tr><th>Workspace:</th><td>${PRETTY_CWS:-$CWS}"


3374         elif [[ -n $manpage ]]; then
3375                 print " --- ---- ---"
3376         fi
3377 
3378         print "</p>"
3379 
3380         # Insert delta comments
3381         print "<blockquote><pre>"
3382         getcomments html $P $PP
3383         print "</pre>"
3384 
3385         # Add additional comments comment
3386         print "<!-- Add comments to explain changes in $P here -->"
3387 
3388         # Add count of changes.
3389         if [[ -f $F.count ]]; then
3390             cat $F.count
3391             rm $F.count
3392         fi
3393 
3394         if [[ $SCM_MODE == "unknown" ]]; then

3395                 # Include warnings for important file mode situations:
3396                 # 1) New executable files
3397                 # 2) Permission changes of any kind
3398                 # 3) Existing executable files
3399                 old_mode=
3400                 if [[ -f $WDIR/raw_files/old/$PP ]]; then
3401                         old_mode=`get_file_mode $WDIR/raw_files/old/$PP`
3402                 fi
3403 
3404                 new_mode=
3405                 if [[ -f $WDIR/raw_files/new/$P ]]; then
3406                         new_mode=`get_file_mode $WDIR/raw_files/new/$P`
3407                 fi
3408 
3409                 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3410                         print "<span class=\"chmod\">"
3411                         print "<p>new executable file: mode $new_mode</p>"
3412                         print "</span>"
3413                 elif [[ -n "$old_mode" && -n "$new_mode" &&
3414                     "$old_mode" != "$new_mode" ]]; then