Print this page
3810 remove support for teamware from webrev

Split Close
Expand all
Collapse all
          --- old/usr/src/tools/scripts/webrev.sh
          +++ new/usr/src/tools/scripts/webrev.sh
↓ open down ↓ 18 lines elided ↑ open up ↑
  19   19  #
  20   20  # CDDL HEADER END
  21   21  #
  22   22  
  23   23  #
  24   24  # Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  25   25  #
  26   26  
  27   27  # Copyright 2008, 2010, Richard Lowe
  28   28  # Copyright 2012 Marcel Telka <marcel@telka.sk>
       29 +# Copyright 2014 Bart Coddens <bart.coddens@gmail.com>
  29   30  
  30   31  #
  31   32  # This script takes a file list and a workspace and builds a set of html files
  32   33  # suitable for doing a code review of source changes via a web page.
  33   34  # Documentation is available via the manual page, webrev.1, or just
  34   35  # type 'webrev -h'.
  35   36  #
  36   37  # Acknowledgements to contributors to webrev are listed in the webrev(1)
  37   38  # man page.
  38   39  #
↓ open down ↓ 1386 lines elided ↑ open up ↑
1425 1426  
1426 1427          print "$HTML<head>$STDHEAD"
1427 1428          print "<title>$WNAME $WHICH $TNAME</title>"
1428 1429          print "<body id=\"SUNWwebrev\">"
1429 1430          print "<pre>"
1430 1431          html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1431 1432          print "</pre></body></html>"
1432 1433  }
1433 1434  
1434 1435  #
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 1436  # comments_from_wx {text|html} filepath
1485 1437  #
1486 1438  # Given the pathname of a file, find its location in a "wx" active
1487 1439  # file list and print the following comment.  Output is either text or
1488 1440  # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1489 1441  # are turned into URLs.
1490 1442  #
1491 1443  # This is also used with Mercurial and the file list provided by hg-active.
1492 1444  #
1493 1445  comments_from_wx()
↓ open down ↓ 35 lines elided ↑ open up ↑
1529 1481  
1530 1482          if [[ -n $Nflag ]]; then
1531 1483                  return
1532 1484          fi
1533 1485          #
1534 1486          # Mercurial support uses a file list in wx format, so this
1535 1487          # will be used there, too
1536 1488          #
1537 1489          if [[ -n $wxfile ]]; then
1538 1490                  comments_from_wx $fmt $p
1539      -        else
1540      -                if [[ $SCM_MODE == "teamware" ]]; then
1541      -                        comments_from_teamware $fmt $pp $p
1542      -                fi
1543 1491          fi
1544 1492  }
1545 1493  
1546 1494  #
1547 1495  # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1548 1496  #
1549 1497  # Print out Code Inspection figures similar to sccs-prt(1) format.
1550 1498  #
1551 1499  function printCI
1552 1500  {
↓ open down ↓ 158 lines elided ↑ open up ↑
1711 1659            while (getline) {
1712 1660                  if (NF == 0) { c = -c; continue }
1713 1661                  if (c > 0) print
1714 1662            }
1715 1663          }' $wxfile > $FLIST
1716 1664  
1717 1665          print " Done."
1718 1666  }
1719 1667  
1720 1668  #
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 1669  # Call hg-active to get the active list output in the wx active list format
1760 1670  #
1761 1671  function hg_active_wxfile
1762 1672  {
1763 1673          typeset child=$1
1764 1674          typeset parent=$2
1765 1675  
1766 1676          TMPFLIST=/tmp/$$.active
1767 1677          $HG_ACTIVE -w $child -p $parent -o $TMPFLIST
1768 1678          wxfile=$TMPFLIST
↓ open down ↓ 140 lines elided ↑ open up ↑
1909 1819  }
1910 1820  
1911 1821  function look_for_prog
1912 1822  {
1913 1823          typeset path
1914 1824          typeset ppath
1915 1825          typeset progname=$1
1916 1826  
1917 1827          ppath=$PATH
1918 1828          ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1919      -        ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
     1829 +        ppath=$ppath:/opt/onbld/bin
1920 1830          ppath=$ppath:/opt/onbld/bin/`uname -p`
1921 1831  
1922 1832          PATH=$ppath prog=`whence $progname`
1923 1833          if [[ -n $prog ]]; then
1924 1834                  print $prog
1925 1835          fi
1926 1836  }
1927 1837  
1928 1838  function get_file_mode
1929 1839  {
↓ open down ↓ 1 lines elided ↑ open up ↑
1931 1841                  if (@stat = stat($ARGV[0])) {
1932 1842                          $mode = $stat[2] & 0777;
1933 1843                          printf "%03o\n", $mode;
1934 1844                          exit 0;
1935 1845                  } else {
1936 1846                          exit 1;
1937 1847                  }
1938 1848              ' $1
1939 1849  }
1940 1850  
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 1851  function build_old_new_mercurial
2014 1852  {
2015 1853          typeset olddir="$1"
2016 1854          typeset newdir="$2"
2017 1855          typeset old_mode=
2018 1856          typeset new_mode=
2019 1857          typeset file
2020 1858  
2021 1859          #
2022 1860          # Get old file mode, from the parent revision manifest entry.
↓ open down ↓ 170 lines elided ↑ open up ↑
2193 2031          typeset CWS=$5
2194 2032          typeset DIR=$6
2195 2033          typeset F=$7
2196 2034  
2197 2035          typeset olddir="$WDIR/raw_files/old"
2198 2036          typeset newdir="$WDIR/raw_files/new"
2199 2037  
2200 2038          mkdir -p $olddir/$PDIR
2201 2039          mkdir -p $newdir/$DIR
2202 2040  
2203      -        if [[ $SCM_MODE == "teamware" ]]; then
2204      -                build_old_new_teamware "$olddir" "$newdir"
2205      -        elif [[ $SCM_MODE == "mercurial" ]]; then
     2041 +        if [[ $SCM_MODE == "mercurial" ]]; then
2206 2042                  build_old_new_mercurial "$olddir" "$newdir"
2207 2043          elif [[ $SCM_MODE == "git" ]]; then
2208 2044                  build_old_new_git "$olddir" "$newdir"
2209 2045          elif [[ $SCM_MODE == "subversion" ]]; then
2210 2046                  build_old_new_subversion "$olddir" "$newdir"
2211 2047          elif [[ $SCM_MODE == "unknown" ]]; then
2212 2048                  build_old_new_unknown "$olddir" "$newdir"
2213 2049          fi
2214 2050  
2215 2051          if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
↓ open down ↓ 23 lines elided ↑ open up ↑
2239 2075          -o <outdir>: Output webrev to specified directory.
2240 2076          -p <compare-against>: Use specified parent wkspc or basis for comparison
2241 2077          -t <remote_target>: Specify remote destination for webrev upload
2242 2078          -U: upload the webrev to remote destination
2243 2079          -w <wxfile>: Use specified wx active file.
2244 2080  
2245 2081  Environment:
2246 2082          WDIR: Control the output directory.
2247 2083          WEBREV_TRASH_DIR: Set directory for webrev delete.
2248 2084  
2249      -SCM Specific Options:
2250      -        TeamWare: webrev [common-options] -l [arguments to 'putback']
2251      -
2252 2085  SCM Environment:
2253 2086          CODEMGR_WS: Workspace location.
2254 2087          CODEMGR_PARENT: Parent workspace location.
2255 2088  '
2256 2089  
2257 2090          exit 2
2258 2091  }
2259 2092  
2260 2093  #
2261 2094  #
↓ open down ↓ 146 lines elided ↑ open up ↑
2408 2241          exit 1
2409 2242  fi
2410 2243  
2411 2244  #
2412 2245  # For the invocation "webrev -n -U" with no other options, webrev will assume
2413 2246  # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2414 2247  # $(basename ${CWS}).  So we need to get CWS set before we skip any remaining
2415 2248  # logic.
2416 2249  #
2417 2250  $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
     2251 +if [[ $SCM_MODE == "mercurial" ]]; then
2434 2252          #
2435 2253          # Mercurial priorities:
2436 2254          # 1. hg root from CODEMGR_WS environment variable
2437 2255          # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under
2438 2256          #    usr/closed when we run webrev
2439 2257          # 2. hg root from directory of invocation
2440 2258          #
2441 2259          if [[ ${PWD} =~ "usr/closed" ]]; then
2442 2260                  testparent=${CODEMGR_WS}/usr/closed
2443 2261                  # If we're in OpenSolaris mode, we enforce a minor policy:
↓ open down ↓ 105 lines elided ↑ open up ↑
2549 2367          else
2550 2368                  flist_mode="auto"
2551 2369          fi
2552 2370  fi
2553 2371  
2554 2372  #
2555 2373  # Before we go on to further consider -l and -w, work out which SCM we think
2556 2374  # is in use.
2557 2375  #
2558 2376  case "$SCM_MODE" in
2559      -teamware|mercurial|git|subversion)
     2377 +mercurial|git|subversion)
2560 2378          ;;
2561 2379  unknown)
2562 2380          if [[ $flist_mode == "auto" ]]; then
2563 2381                  print -u2 "Unable to determine SCM in use and file list not specified"
2564 2382                  print -u2 "See which_scm(1) for SCM detection information."
2565 2383                  exit 1
2566 2384          fi
2567 2385          ;;
2568 2386  *)
2569 2387          if [[ $flist_mode == "auto" ]]; then
2570 2388                  print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2571 2389                  exit 1
2572 2390          fi
2573 2391          ;;
2574 2392  esac
2575 2393  
2576 2394  print -u2 "   SCM detected: $SCM_MODE"
2577 2395  
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
     2396 +if [[ -n $wflag ]]; then
2594 2397          #
2595 2398          # If the -w is given then assume the file list is in Bonwick's "wx"
2596 2399          # command format, i.e.  pathname lines alternating with SCCS comment
2597 2400          # lines with blank lines as separators.  Use the SCCS comments later
2598 2401          # in building the index.html file.
2599 2402          #
2600 2403          shift $(($OPTIND - 1))
2601 2404          wxfile=$1
2602 2405          if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2603 2406                  if [[ -r $CODEMGR_WS/wx/active ]]; then
↓ open down ↓ 22 lines elided ↑ open up ↑
2626 2429  fi
2627 2430  
2628 2431  if [[ $# -gt 0 ]]; then
2629 2432          print -u2 "WARNING: unused arguments: $*"
2630 2433  fi
2631 2434  
2632 2435  #
2633 2436  # Before we entered the DO_EVERYTHING loop, we should have already set CWS
2634 2437  # and CODEMGR_WS as needed.  Here, we set the parent workspace.
2635 2438  #
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
     2439 +if [[ $SCM_MODE == "mercurial" ]]; then
2707 2440          #
2708 2441          # Parent can either be specified with -p
2709 2442          # Specified with CODEMGR_PARENT in the environment
2710 2443          # or taken from hg's default path.
2711 2444          #
2712 2445  
2713 2446          if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2714 2447                  codemgr_parent=$CODEMGR_PARENT
2715 2448          fi
2716 2449  
↓ open down ↓ 959 lines elided ↑ open up ↑
3676 3409  
3677 3410          print "<!-- Add comments to explain changes in $P here -->"
3678 3411  
3679 3412          # Add count of changes.
3680 3413  
3681 3414          if [[ -f $F.count ]]; then
3682 3415              cat $F.count
3683 3416              rm $F.count
3684 3417          fi
3685 3418  
3686      -        if [[ $SCM_MODE == "teamware" ||
3687      -            $SCM_MODE == "mercurial" ||
     3419 +        if [[ $SCM_MODE == "mercurial" ||
3688 3420              $SCM_MODE == "unknown" ]]; then
3689 3421  
3690 3422                  # Include warnings for important file mode situations:
3691 3423                  # 1) New executable files
3692 3424                  # 2) Permission changes of any kind
3693 3425                  # 3) Existing executable files
3694 3426  
3695 3427                  old_mode=
3696 3428                  if [[ -f $WDIR/raw_files/old/$PP ]]; then
3697 3429                          old_mode=`get_file_mode $WDIR/raw_files/old/$PP`
↓ open down ↓ 55 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX