#!/bin/sh # mvim: The Vim Installation Maintenance Tool # Copyright (C) 2003 Stephen Thomas # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. generichelp () { cat <.-*.tar*", while patched source archives have the form "-pl.tar*". See below for an explanation of , and . Source Set This is a complete set of Vim sources. An original, or pristine, source set is the result of unpacking a complete set of original source archives. A patched source set is an original source set which has had some pre-modification patches and update patches applied to it. The original source archives unpack all their contents into heirarchy under a single directory, the name of which is the . The contents of a patched source set are also under a single directory, although it's name is more complex, as it takes the form "-pl". A is a name derived from the various pre-modification patches which have been applied, while indicates the most recent update patch which has been applied. A is always a 3-digit code. A special case of a patched source set is a patchlevel-000 source set, which is an original source set with only pre-modification patches applied to it. Pre-Modification Patch A pre-modification patch is a change that gets applied to an original source set, before any update patches are applied. Unlike update patches, they are not official in any way. They are usually locally constructed and tend to be used to modify Makefiles to alter configuration setups. A pre-modification patch takes the form ".-", where and identify the Vim version the patch relates to and is some mnemonic sequence of characters (which must not contain a hyphen). A part of a patched source set directory name or archive name is the parts of all pre-modification patches used, each with a leading hyphen and concatenated together in the order the patches were applied. Update Patch An update patch is an official (ie, released by the Vim maintainer) change to a particular Vim version, usually in order to correct a fault or add a new feature. They take the form "..", where again and identify the Vim version the patch is for, and is a 3-digit numeric code, with values ranging over 001-999. Update patches are applied in numeric order, and the entry of a patched source set or archive indicates the most recently applied update patch. As an example of all the above, consider someone who has ~/myvim as their reference directory. Looking at the contents of ~/myvim, we might see the following: 6.1.001 -- These are update patch files, for patches 001 through 6.1.002 -- 010 against Vim version 6.1. 6.1.003 6.1.004 6.1.005 6.1.006 6.1.007 6.1.008 6.1.009 6.1.010 6.1-chccomp -- pre-modification patch to alter C compiler options. 6.1-multibyte -- pre-modification patch to enable multibyte. vim-6.1-extra.tar.gz -- Original source archives. When unpacked, these vim-6.1-lang.tar.gz -- create a original source set in directory vim61. vim-6.1-rt1.tar.gz vim-6.1-rt2.tar.gz vim-6.1-src1.tar.gz vim-6.1-src2.tar.gz vim61-chccomp-multibyte-pl010 -- A patched source set with all the above -- pre-modification and update patches -- applied. vim61-chccomp-multibyte-pl010.tar.bz2 -- A patched source archive the -- contents of the above source set. work -- The working directory. work/vim61-chccomp-multibyte-pl010 -- A made patched source set. ENDTEXT } commandhelp () { cat </work". See also global option -r. Options in the config file are specified exactly as they are on the command line, except that they can be spread out over more than one line. Options in the config file are always processed before options on the command line, except for options -s and -S. The reference and working directories must be different locations (and not just different names) - mvim goes to some effort to ensure this is so. The reference directory must exist. However, if the working directory does not exist, mvim will try to create it. ENDTEXT } updatehelp () { cat <&2 exit 1 } failopt () { echo "ERROR: option -$2 invalid with '$1' command" >&2 exit 1 } warn () { echo "WARNING: $*" >&2 } configparameters () { local done="" local config=$1 shift OPTIND=1 while getopts ":a:dDgGMqQr:s:Su:w:" arg; do case "$arg" in a) [ "$config" != "onlyconfig" ] && ftpsite=$OPTARG ;; d) [ "$config" != "onlyconfig" ] && remotedirlist="$stablepatchdir $unstablepatchdir" ;; D) [ "$config" != "onlyconfig" ] && remotedirlist="$unstablepatchdir" ;; g) [ "$config" != "onlyconfig" -a -n "$gzipprog" ] && comprtype=".gz" ;; G) [ "$config" != "onlyconfig" ] && comprtype="" ;; M) [ "$config" != "onlyconfig" ] && wantpremods="" ;; q) [ "$config" != "onlyconfig" ] && { quiet=true veryquiet="" } ;; Q) [ "$config" != "onlyconfig" ] && { quiet=true veryquiet=true } ;; r) [ "$config" != "onlyconfig" ] && refdir=$OPTARG ;; s) [ "$config" = "onlyconfig" ] && configfile=$OPTARG ;; S) [ "$config" = "onlyconfig" ] && configfile="" ;; u) [ "$config" != "onlyconfig" ] && email=$OPTARG ;; w) [ "$config" != "onlyconfig" ] && workdir=$OPTARG ;; \?) fail "invalid option '$OPTARG'" ;; :) fail "missing required argument for '$OPTARG'" ;; esac done shiftby=$(($OPTIND - 1)) } commandparameters () { local done="" local cmd=$1 shift OPTIND=1 while getopts ":AbBcfiIm:nRv:x" arg; do case "$arg" in A) [ "$cmd" = "new" ] && ftpsite="" [ "$cmd" = "update" ] && ftpsite="" [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; b) [ "$cmd" = "new" ] && wantbuild=true [ "$cmd" = "update" ] && wantbuild=true [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; B) [ "$cmd" = "new" ] && failopt $cmd $arg [ "$cmd" = "update" ] && cleanbuild=true [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; c) [ "$cmd" = "new" ] && failopt $cmd $arg [ "$cmd" = "update" ] && failopt $cmd $arg [ "$cmd" = "download" ] && checkonly=true [ "$cmd" = "purge" ] && failopt $cmd $arg ;; f) [ "$cmd" = "new" ] && failopt $cmd $arg [ "$cmd" = "update" ] && failopt $cmd $arg [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && fullpurge=true ;; i) [ "$cmd" = "new" ] && wantinstall=user [ "$cmd" = "update" ] && wantinstall=user [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; I) [ "$cmd" = "new" ] && wantinstall=root [ "$cmd" = "update" ] && wantinstall=root [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; m) [ "$cmd" = "new" ] && modset="${modset}-$OPTARG" [ "$cmd" = "update" ] && modset="${modset}-$OPTARG" [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; n) [ "$cmd" = "new" ] && wantbuild=false [ "$cmd" = "update" ] && wantbuild=false [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; R) [ "$cmd" = "new" ] && refdirreadonly=false [ "$cmd" = "update" ] && refdirreadonly=false [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; v) [ "$cmd" = "new" ] && version=$OPTARG [ "$cmd" = "update" ] && version=$OPTARG [ "$cmd" = "download" ] && version="$version $OPTARG" [ "$cmd" = "purge" ] && version=$OPTARG ;; x) [ "$cmd" = "new" ] && findpatches="" [ "$cmd" = "update" ] && findpatches="" [ "$cmd" = "download" ] && failopt $cmd $arg [ "$cmd" = "purge" ] && failopt $cmd $arg ;; \?) fail "invalid option '$OPTARG'" ;; :) fail "missing required argument for '$OPTARG'" ;; esac done shiftby=$(($OPTIND - 1)) } qmsg () { [ -z "$quiet" ] && echo "$@" } vqmsg () { [ -z "$veryquiet" ] && echo "$@" } runcmd () { cmd=$1 shift $cmd "$@" [ $? -gt 0 ] && fail "command failure: $cmd $@" } rungrep () { # runcmd cannot be used to run grep, because a return value of 0 or 1 # indicates success as far as we are concerned, while 2 indicates a # fault. $grepprog "$@" [ $? -gt 1 ] && fail "command failure: $grepprog $@" } trywhich () { $* which >/dev/null 2>&1 if [ $? -eq 0 ]; then echo $* else echo "" fi } findwhich () { local wp # Assume that a simple which command will work, then try adding # options. If that does not work, bail out. wp=$(which which) [ -n "$wp" ] || fail "cannot find which" whichcmd="$(trywhich $wp --skip-alias --skip-functions --skip-dot --skip-tilde)" [ -n "$whichcmd" ] && return whichcmd="$(trywhich $wp)" [ -n "$whichcmd" ] && return fail "$wp is broken" } findprog () { $whichcmd $@ 2>/dev/null } findprogfail () { local ex=$(findprog $1) [ -z "$ex" ] && fail "cannot find $1" echo $ex } canonicaldata () { # The canonical data of a file is something which uniquely identifies # file or directory, no matter what the path of the thing is. If the # appropriate tools are available, we choose a combination of the name # of the device node corresponding to the mounted filesystem the item # resides in, and the inode of the item. If the tools are not # available, just use the names and hope. if [ -z "$dfprog" -o -z "$tailprog" -o -z "$cutprog" -o -z "$lsprog" ]; then echo $1 else local fs=$(runcmd $dfprog -P $1 | runcmd $tailprog -n1 | runcmd $cutprog "-d " -f1) local in=$(runcmd $lsprog -id $1 | runcmd $cutprog "-d " -f2) echo $fs-$in fi } stripcomments () { local output=$(runcmd $sedprog -e 's/#.*$//' -e '/^\s*$/d' <$1) local sep="" for i in $output; do echo -n "$sep" $BASH -c "echo -n $i" sep=" " done } nextdigit () { runcmd $trprog "0123456789" "1234567890" <<<$1 } nextpatch () { # Given a 3-digit patch code, determine the code for the next # patch. There has got to be a better way of doing this ... local nu nt nh nu=$(nextdigit ${1:2:1}) if [ "$nu" = "0" ]; then nt=$(nextdigit ${1:1:1}) if [ "$nt" = "0" ]; then nh=$(nextdigit ${1:0:1}) [ "$nh" = "0" ] && fail "asked for patchlevel 999 successor" else nh=${1:0:1} fi else nt=${1:1:1} nh=${1:0:1} fi echo "$nh$nt$nu" } nextpremod () { local next=${1#-*-} [ "$next" != "$1" ] && echo -n "-$next" } emitpatchpair () { if [ "$1" = "$2" ]; then echo -n $1 else local s=$(nextpatch $1) if [ "$2" = "$s" ]; then echo -n "$1,$2" else echo -n "$1-$2" fi fi } emitpatchseries () { if [ -z "$1" ]; then echo "none" else local base=$1 local prev=$base local expected=$(nextpatch $base) local sep="" shift for item; do if [ "$item" != "$expected" ]; then echo -n $sep emitpatchpair $base $prev sep="," base=$item fi prev=$item expected=$(nextpatch $prev) done echo -n $sep emitpatchpair $base $prev fi } expandpatchpair () { local first last if [ "$1" \< "$2" ]; then first=$1 last=$2 else first=$2 last=$1 fi while [ "$first" \< "$last" ]; do [ "$first" \> "$frompl" -a -f $refdir/$version.$first ] && echo $first first=$(nextpatch $first) done [ "$first" \> "$frompl" -a -f $refdir/$version.$first ] && echo $first } expandpatchseries () { local pset="$1,--end-sentinel--" local next local first last while [ "$pset" != "--end-sentinel--" ]; do fp=${pset%,*} first=${fp%-*} last=${fp#*-} expandpatchpair $first $last pset=${pset#*,} done } packform () { # Choose amongst $2, $3 or $4 depending on whether $1 appears to # be a gzipped file, a bzipped file, or something else. case "$1" in *.gz) echo $2;; *.bz2) echo $3;; *) echo $4 esac } compressarchive () { local pf=$(packform $2 $gzipprog $bzipprog "") if [ -n "$pf" ]; then if [ "$pf" = "-notfound-" ]; then warn "unable to compress $1" else runcmd $pf $1 fi fi } decompressarchive () { local pf=$(packform $1 $gzipprog $bzipprog "") if [ -n "$pf" ]; then if [ "$pf" = "-notfound-" ]; then fail "unable to decompress $1" else runcmd $pf -c -d $1 fi else runcmd $catprog $1 fi } purgeconstructed () { for d in $1/$2$3-pl*; do qmsg " removing $d" runcmd $rmprog -rf $d done } validatedirectories () { # If no directories are configured, use some sensible defaults. [ -z "$refdir" ] && refdir="." [ -z "$workdir" ] && workdir="$refdir/work" # Check if the reference directory actually exists and is a directory. # Not much we can do if it doesn't or isn't. if [ ! -d $refdir ]; then if [ -e $refdir ]; then fail "$refdir is not a directory" else fail "reference directory $refdir does not exist" fi fi # If the working directory does not exist, we are allowed to try and # create it. Doing so, however, implies a full build is required. if [ ! -e $workdir ]; then [ -n "$mkdirprog" ] && runcmd $mkdirprog -p $workdir [ ! -d $workdir ] && fail "could not create working directory $workdir" [ "$command" = "update" ] && command=new else [ ! -d $workdir ] && fail "$workdir is not a directory" fi # Ensure, as far as we can, that the reference directory and the # working directory are different places. [ "$(canonicaldata $refdir)" = "$(canonicaldata $workdir)" ] && fail "$refdir and $workdir are the same site" if [ -z "$email" -a -n "$ftpsite" ]; then warn "no email address for anonymous FTP set, remote patch checking disabled" ftpsite="" fi vqmsg "Reference directory: $refdir" vqmsg "Working directory: $workdir" } inferversion () { if [ -z "$version" ]; then # No version specified, so work out which version we are interested in # by looking at what original archives are available. local arc version=$( { for arc in $(rungrep -v -E -e 'pl[0-9]{3}\.tar.*$' <<<"$refdir/vim-*.tar*"); do runcmd $sedprog -e 's/^.*vim-\([0-9]\+\)\.\([0-9][0-9A-Za-z]*\).*$/\1\.\2/' <<<$arc done } | runcmd $sortprog -u) # If no version appears to be available, we have to assume the # reference directory was incorrect. [ -z "$version" ] && fail "$refdir does not contain any Vim source archive components" fi } findrootname () { # Determine the root name, by looking at the names of the distribution # archives. local ver=$1 local arc rootname=$( { for arc in $(rungrep -v -E -e 'pl[0-9]{3}\.tar.*$' <<<"$refdir/vim-$ver-*.tar*"); do # Examine the contents of the archive. Look at the top level element # in the path. decompressarchive $arc | runcmd $tarprog tf - | runcmd $sedprog -e 's/^\([^/]*\).*/\1/' done } | runcmd $sortprog -u) [ $(listlen $rootname) -gt 1 ] && fail "multiple root names found: $rootname" # If no root name has been found, we have to assume that the user gave # an invalid version. [ -z "$rootname" ] && fail "could not find root name, is version $ver valid?" } scanforpremodsets () { local pl base for pl in $1/$rootname*-pl*; do base=${pl#*/$rootname} echo ${base%-pl*} done } validateprepatches () { local ver=$1 if [ -n "$wantpremods" ]; then if [ -z "$modset" ]; then # No modset specified. First, scan for existing patchlevel # distributions, to see what has been used before. modset=$( { scanforpremodsets $workdir scanforpremodsets $refdir } | runcmd $sortprog -u) if [ $(listlen $modset) -gt 1 ]; then echo "Local pre-modification: $modset" echo " more that one combination found, please choose one using -m" exit 0 fi if [ -z "$modset" ]; then # Could not find any PLD, so look for actual PMF's. local m for m in $refdir/$ver-*; do modset="$modset$(runcmd $sedprog -e 's/^.*\/[0-9]\+\.[0-9][0-9A-Za-z]*\(-.*\)$/\1/' <<<$m)" done fi return fi # Go through all the items in the modset, excluding ones that don't # actually exist. local mods=$modset local thismod next modset="" while [ -n "$mods" ]; do thismod=$(runcmd $cutprog -d- -f2 <<<$mods) if [ -f $refdir/$ver-$thismod ]; then modset="$modset-$thismod" else warn "pre-modification patch $ver-$thismod does not exist, excluding" fi mods=$(nextpremod $mods) done else # No pre-modification patches are wanted, even if some are available. modset="" fi } scanforpatchlevel () { local prefix=$2 local cdist local pl="" for cdist in $1/$prefix-pl*; do [ -d $cdist ] && pl=$(runcmd $sedprog -e 's/^.*-pl\([0-9]*\)$/\1/' <<<$cdist) done echo -n $pl } infercurrentpatchlevel () { # First try looking in the reference directory. local rpl=$(scanforpatchlevel "$refdir" $1) local wpl=$(scanforpatchlevel "$workdir" $1) if [ -z "$rpl" ]; then # No patchlevel distribution in the reference directory. if [ -z "$wpl" ]; then # No patchlevel distribution in the working directory, # either. Assume that a full update is required. command=new else # There was a patchlevel distribution in the working # directory, but not in the reference directory. From # this we infer that the reference directory should be # considered read-only. refdirreadonly=true frompl=$wpl fi elif [ "$rpl" != "$wpl" ]; then # There was a patchlevel distribution in the reference directory, # but nothing in the working directory, or the patchlevel # distributions in the two directories were different. We have # to assume that a full update is required, but we don't assume # that reference directory is read-only. command=new else # The reference and working directories contain corresponding # patchlevel distributions. frompl=$rpl fi } downloadnewpatches () { # Create a couple of temporary files, one for an lftp script and the # other for the output of the lftp command. local ver=$1 local dir=$2 local tscript=$(mktemp -q /tmp/rmk.XXXXXX) [ $? -gt 0 ] && { warn "could not create temporary script file" return } local tfile=$(mktemp -q /tmp/rmk.XXXXXX) [ $? -gt 0 ] && { warn "could not create temporary output file" runcmd $rmprog -f $tscript return } # Write and execute a script to get a list of available patch files. echo "open -u anonymous,$email $ftpsite/$dir" >$tscript echo "nlist" >>$tscript echo "close -a" >>$tscript $lftpprog -f $tscript >$tfile [ $? -gt 0 ] && { warn "could not obtain patch list from $ftpsite/$dir" runcmd $rmprog -f $tscript $tfile return } # Of the files found, work out which ones are really patch files, and # of those, which ones we don't already have. Start building a script # which will download these we need to get. local plev major minor pattern local anynew="" major=$(runcmd $cutprog -d. -f1 <<<$ver) minor=$(runcmd $cutprog -d. -f2 <<<$ver) for plev in $(rungrep -E -e "^$major\.$minor\.[0-9]+$" <$tfile | runcmd $cutprog -d. -f3); do if [ ! -f $refdir/$major.$minor.$plev ]; then [ -z "$anynew" ] && echo "open -u anonymous,$email $ftpsite/$dir" >$tscript anynew="$anynew $plev" echo "get $ver.$plev -o $refdir/$ver.$plev" >>$tscript fi done if [ -z "$checkonly" -a -n "$anynew" ]; then # If there were any to get, actually go an download them. echo "close -a" >>$tscript $lftpprog -f $tscript >/dev/null 2>&1 # The above command may have failed, or not been completely # successful. Of the files we wanted, see how many we appeared # to get. local np=$anynew anynew="" for plev in $np; do if [ -f $refdir/$ver.$plev ]; then anynew="$anynew $plev" else warn "New patch $ver.$plev not downloaded" fi done fi rm -f $tscript $tfile echo -n $anynew } fetchfreshpatches () { remotepatchset="" if [ -n "$ftpsite" ]; then local dir local ver=$1 for dir in $remotedirlist; do remotepatchset="$remotepatchset$(downloadnewpatches $ver $dir)" remotepatchset="$remotepatchset$(downloadnewpatches $ver $dir/$ver)" done fi } determinenewpatchlevels () { patchset="" local p if [ -n "$findpatches" ]; then local pcode for p in $refdir/$version.*; do pcode=$(runcmd $sedprog -e 's/^.*\/[0-9][0-9]*\.[0-9][0-9A-Za-z]*\.\([0-9]*\)$/\1/' <<<$p) if [ "$pcode" \> "$frompl" ]; then patchset="$patchset $pcode" fi done else local pspec patchset=$( { for item; do if [[ "$item" = -* ]]; then pspec="$(nextpatch $frompl)$item" else pspec=$item fi expandpatchseries $pspec done } | runcmd $sortprog -u) fi } applypatchlevel () { cd $1 runcmd $mvprog $rootname$modset-pl$2 $rootname$modset-pl$3 cd $rootname$modset-pl$3 runcmd $patchprog -s -f -p0 <$refdir/$version.$3 } updateinstallation () { local oldpl=$frompl local finalpl=$frompl local newpl="" local defaultbuild=$1 fetchfreshpatches $version determinenewpatchlevels $remainingargs if [ -n "$ftpsite" ]; then vqmsg "Patches downloaded: $(emitpatchseries $remotepatchset)" fi vqmsg "Patches to apply: $(emitpatchseries $patchset)" [ -n "$patchset" ] && vqmsg "Upgrading patchlevels" # For each additional patch for newpl in $patchset; do qmsg " applying patch $version.$newpl" [ -z "$refdirreadonly" -a -d $refdir/$rootname$modset-pl$oldpl ] && applypatchlevel $refdir $oldpl $newpl applypatchlevel $workdir $oldpl $newpl oldpl=$newpl finalpl=$newpl # If a patch has been applied, then a build is implied. defaultbuild=true done if [ -z "$refdirreadonly" -a -n "$newpl" ]; then cd $refdir vqmsg "Removing archive $rootname$modset-pl$frompl.tar*, constructing new archive $rootname$modset-pl$newpl.tar$comprtype" runcmd $rmprog $rootname$modset-pl$frompl.tar* runcmd $tarprog "cbf" 1 $rootname$modset-pl$newpl.tar $rootname$modset-pl$newpl compressarchive $rootname$modset-pl$newpl.tar $comprtype fi # Work out whether or not a build is required. [ -z "$wantbuild" ] && wantbuild=$defaultbuild if [ -n "$cleanbuild" ]; then vqmsg "Cleaning $rootname$modset-pl$finalpl" cd $workdir/$rootname$modset-pl$finalpl/src runcmd $makeprog distclean fi if [ "$wantbuild" != "false" ]; then vqmsg "Building $rootname$modset-pl$finalpl" cd $workdir/$rootname$modset-pl$finalpl/src runcmd $makeprog fi if [ -n "$wantinstall" ]; then vqmsg "Installing $rootname$modset-pl$finalpl" cd $workdir/$rootname$modset-pl$finalpl/src if [ "$wantinstall" = "root" -a -n "$sudoprog" ]; then runcmd $sudoprog $makeprog install else runcmd $makeprog install fi fi } shopt -sq nullglob # By default we assume the update is incremental and that we don't # want a build. stablepatchdir="pub/vim/patches" unstablepatchdir="pub/vim/unstable/patches" quiet="" veryquiet="" findpatches=true cleanbuild="" wantbuild="" wantinstall="" fullpurge="" modset="" wantpremods=true refdir="" workdir="" email="" checkonly="" refdirreadonly="" comprtype=".bz2" remotedirlist=$stablepatchdir ftpsite="ftp://ftp.vim.org" configfile=$HOME/.mvimrc # Check which compression programs we have available findwhich catprog=$(findprogfail "cat") cutprog=$(findprogfail "cut") sedprog=$(findprogfail "sed") grepprog=$(findprogfail "grep") trprog=$(findprogfail "tr") rmprog=$(findprogfail "rm") mvprog=$(findprogfail "mv") tarprog=$(findprogfail "tar") patchprog=$(findprogfail "patch") makeprog=$(findprogfail "make") sortprog=$(findprogfail "sort") sudoprog=$(findprog "sudo") dfprog=$(findprog "df") lsprog=$(findprog "ls") tailprog=$(findprog "tail") mkdirprog=$(findprog "mkdir") gzipprog=$(findprog "gzip") bzipprog=$(findprog "bzip2") lftpprog=$(findprog "lftp") if [ -n "$bzipprog" ] then comprtype=".bz2" if [ -z "$gzipprog" ] then gzipprog="-notfound-" fi elif [ -n "$gzipprog" ] then bzipprog="-notfound-" comprtype=".gz" else bzipprog="-notfound-" gzipprog="-notfound-" comprtype="" fi # Handle any pre-command options. First, look for any -s parameter, # as this defines the configuration file, if any. If one is found, # process the contents of that file before dealing with the remaining # pre-command parameters specified on the command line. configparameters onlyconfig "$@" [ -n "$configfile" -a -f $configfile ] && { args=$(stripcomments $configfile) configparameters noconfig $args } configparameters noconfig "$@" shift $shiftby # Work out what command we are using, and then handle any post-command # options. Note that post-command options cannot be specified in the # config file. command=$1 shift if [ -z "$command" ]; then echo "mvim: Vim Installation Manager" echo "Usage: mvim [global options] command [command options]" echo "For more help, try \"mvim help\"" exit 0 fi validcommand="" [ "$command" = "new" ] && validcommand=true [ "$command" = "update" ] && validcommand=true [ "$command" = "download" ] && validcommand=true [ "$command" = "purge" ] && validcommand=true [ "$command" = "help" ] && validcommand=true [ -z "$validcommand" ] && fail "invalid command '$command'" if [ "$command" = "help" ]; then if [ -z "$1" ]; then generichelp elif [ "$1" = "commands" ]; then commandhelp elif [ "$1" = "terms" ]; then termhelp elif [ "$1" = "options" ]; then optionhelp elif [ "$1" = "update" ]; then updatehelp elif [ "$1" = "new" ]; then newhelp elif [ "$1" = "download" ]; then downloadhelp elif [ "$1" = "purge" ]; then purgehelp fi exit 0 fi # If we do not have lftp, then we cannot use FTP. [ -n "$lftpprog" ] || ftpsite="" # Ensure the reference and working directories are sensibly configured. # This will try to choose some sensible defaults if the config file # or pre-command options did not specify any. validatedirectories commandparameters $command "$@" shift $shiftby remainingargs="$*" inferversion if [ "$command" = "download" ]; then if [ -n "$checkonly" ]; then vqmsg "Mode: check for new patches" else vqmsg "Mode: check for and download new patches" fi for v in $version; do fetchfreshpatches $v vqmsg "New patches: (against $v) $(emitpatchseries $remotepatchset)" done elif [ "$command" = "purge" ]; then # It is not an error if multiple versions are present, we should simply # report what versions are available and do nothing more. [ $(listlen $version) -gt 1 ] && { echo "Versions available: $version" exit 0 } findrootname $version if [ -z "$fullpurge" ]; then vqmsg "Mode: limited purge of version $version" vqmsg "Root name: $rootname" validateprepatches $version vqmsg "Clearing patchlevel distributions and archives" purgeconstructed $workdir $rootname $modset [ -z "$refdirreadonly" ] && purgeconstructed $refdir $rootname $modset else vqmsg "Mode: full purge of version $version" vqmsg "Removing constructed distributions and archives" for i in $refdir/$rootname*-pl* $workdir/$rootname*-pl*; do qmsg " removing $i" runcmd $rmprog -rf $i done vqmsg "Removing original Vim source components:" for i in $refdir/vim-$version*.tar*; do qmsg " removing $i" runcmd $rmprog -rf $i done vqmsg "Removing pre-modification patches:" for i in $refdir/$version-*; do qmsg " removing $i" runcmd $rmprog -rf $i done vqmsg "Removing patchlevel files:" for i in $refdir/$version.*; do qmsg " removing $i" runcmd $rmprog -rf $i done fi else # It is not an error if multiple versions are present, we should simply # report what versions are available and do nothing more. [ $(listlen $version) -gt 1 ] && { echo "Versions available: $version" exit 0 } # The command is either update or new. These have a common # preamble. findrootname $version validateprepatches $version # If the command is update, we need to determine what the current # patchlevel actually is. However, that could determine that # a new build is really required. [ "$command" = "update" ] && infercurrentpatchlevel "$rootname$modset" if [ "$command" = "new" ]; then vqmsg "Mode: build new patchlevel distribution for version $version" vqmsg "Name prefix: $rootname$modset" # For a full build we need to clear out any constructed directories # and archives in the reference and working directories. vqmsg "Clearing patchlevel distributions and archives" purgeconstructed $workdir $rootname $modset [ -z "$refdirreadonly" ] && purgeconstructed $refdir $rootname $modset # Next we unpack the real sources. cd $workdir vqmsg "Unpacking pristine vim-$version" for v in $refdir/vim-$version*.tar*; do qmsg " unpacking component $v" decompressarchive $v | runcmd $tarprog xf - done # Apply any local modifications. cd $workdir/$rootname [ -n "$modset" ] && vqmsg "Applying pre-modification patches" mods=$modset while [ -n "$mods" ]; do thismod=$(runcmd $cutprog -d- -f2 <<<$mods) qmsg " applying patch $version-$thismod" runcmd $patchprog -s -f -p0 <$refdir/$version-$thismod mods=$(nextpremod $mods) done # Rename the unpacked archive, reflecting the local modification and the # initial patch level. runcmd $mvprog $workdir/$rootname $workdir/$rootname$modset-pl000 if [ -z "$refdirreadonly" ]; then # If we are not running with the reference directory read-only, # we should construct the initial reference patchlevel archive, # and unpack a copy of the patchlevel distribution into the # reference directory. vqmsg "Constructing initial reference archive $rootname$modset-pl000.tar$comprtype" cd $workdir runcmd $tarprog cbf 1 $refdir/$rootname$modset-pl000.tar $rootname$modset-pl000 cd $refdir runcmd $tarprog xf $rootname$modset-pl000.tar compressarchive $rootname$modset-pl000.tar $comprtype fi frompl=000 definst=true else vqmsg "Mode: update existing patchlevel distribution for version $version" vqmsg "Name prefix: $rootname$modset" definst=false fi # Finally, update with appropriate patches, optionally build and # install. updateinstallation $definst fi