1238423Sjhb#!/bin/sh 2238423Sjhb# 3238423Sjhb# Copyright (c) 2010 Advanced Computing Technologies LLC 4238423Sjhb# Written by: John H. Baldwin <jhb@FreeBSD.org> 5238423Sjhb# All rights reserved. 6238423Sjhb# 7238423Sjhb# Redistribution and use in source and binary forms, with or without 8238423Sjhb# modification, are permitted provided that the following conditions 9238423Sjhb# are met: 10238423Sjhb# 1. Redistributions of source code must retain the above copyright 11238423Sjhb# notice, this list of conditions and the following disclaimer. 12238423Sjhb# 2. Redistributions in binary form must reproduce the above copyright 13238423Sjhb# notice, this list of conditions and the following disclaimer in the 14238423Sjhb# documentation and/or other materials provided with the distribution. 15238423Sjhb# 16238423Sjhb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17238423Sjhb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18238423Sjhb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19238423Sjhb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20238423Sjhb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21238423Sjhb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22238423Sjhb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23238423Sjhb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24238423Sjhb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25238423Sjhb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26238423Sjhb# SUCH DAMAGE. 27238423Sjhb# 28238423Sjhb# $FreeBSD$ 29238423Sjhb 30238423Sjhb# This is a tool to manage updating files that are not updated as part 31238423Sjhb# of 'make installworld' such as files in /etc. Unlike other tools, 32238423Sjhb# this one is specifically tailored to assisting with mass upgrades. 33238423Sjhb# To that end it does not require user intervention while running. 34238423Sjhb# 35238423Sjhb# Theory of operation: 36238423Sjhb# 37238423Sjhb# The most reliable way to update changes to files that have local 38238423Sjhb# modifications is to perform a three-way merge between the original 39238423Sjhb# unmodified file, the new version of the file, and the modified file. 40238423Sjhb# This requires having all three versions of the file available when 41238423Sjhb# performing an update. 42238423Sjhb# 43238423Sjhb# To that end, etcupdate uses a strategy where the current unmodified 44238423Sjhb# tree is kept in WORKDIR/current and the previous unmodified tree is 45238423Sjhb# kept in WORKDIR/old. When performing a merge, a new tree is built 46238423Sjhb# if needed and then the changes are merged into DESTDIR. Any files 47238423Sjhb# with unresolved conflicts after the merge are left in a tree rooted 48238423Sjhb# at WORKDIR/conflicts. 49238423Sjhb# 50238423Sjhb# To provide extra flexibility, etcupdate can also build tarballs of 51238423Sjhb# root trees that can later be used. It can also use a tarball as the 52238423Sjhb# source of a new tree instead of building it from /usr/src. 53238423Sjhb 54238423Sjhb# Global settings. These can be adjusted by config files and in some 55238423Sjhb# cases by command line options. 56238423Sjhb 57238423Sjhb# TODO: 58238423Sjhb# - automatable conflict resolution 59238423Sjhb# - a 'revert' command to make a file "stock" 60238423Sjhb 61238423Sjhbusage() 62238423Sjhb{ 63238423Sjhb cat <<EOF 64238423Sjhbusage: etcupdate [-nBF] [-d workdir] [-r | -s source | -t tarball] [-A patterns] 65238423Sjhb [-D destdir] [-I patterns] [-L logfile] [-M options] 66238423Sjhb etcupdate build [-B] [-d workdir] [-s source] [-L logfile] [-M options] 67238423Sjhb <tarball> 68238423Sjhb etcupdate diff [-d workdir] [-D destdir] [-I patterns] [-L logfile] 69238423Sjhb etcupdate extract [-B] [-d workdir] [-s source | -t tarball] [-L logfile] 70238423Sjhb [-M options] 71238423Sjhb etcupdate resolve [-d workdir] [-D destdir] [-L logfile] 72238423Sjhb etcupdate status [-d workdir] [-D destdir] 73238423SjhbEOF 74238423Sjhb exit 1 75238423Sjhb} 76238423Sjhb 77238423Sjhb# Used to write a message prepended with '>>>' to the logfile. 78238423Sjhblog() 79238423Sjhb{ 80238423Sjhb echo ">>>" "$@" >&3 81238423Sjhb} 82238423Sjhb 83238423Sjhb# Used for assertion conditions that should never happen. 84238423Sjhbpanic() 85238423Sjhb{ 86238423Sjhb echo "PANIC:" "$@" 87238423Sjhb exit 10 88238423Sjhb} 89238423Sjhb 90238423Sjhb# Used to write a warning message. These are saved to the WARNINGS 91238423Sjhb# file with " " prepended. 92238423Sjhbwarn() 93238423Sjhb{ 94238423Sjhb echo -n " " >> $WARNINGS 95238423Sjhb echo "$@" >> $WARNINGS 96238423Sjhb} 97238423Sjhb 98238423Sjhb# Output a horizontal rule using the passed-in character. Matches the 99238423Sjhb# length used for Index lines in CVS and SVN diffs. 100238423Sjhb# 101238423Sjhb# $1 - character 102238423Sjhbrule() 103238423Sjhb{ 104238423Sjhb jot -b "$1" -s "" 67 105238423Sjhb} 106238423Sjhb 107238423Sjhb# Output a text description of a specified file's type. 108238423Sjhb# 109238423Sjhb# $1 - file pathname. 110238423Sjhbfile_type() 111238423Sjhb{ 112238423Sjhb stat -f "%HT" $1 | tr "[:upper:]" "[:lower:]" 113238423Sjhb} 114238423Sjhb 115238423Sjhb# Returns true (0) if a file exists 116238423Sjhb# 117238423Sjhb# $1 - file pathname. 118238423Sjhbexists() 119238423Sjhb{ 120238423Sjhb [ -e $1 -o -L $1 ] 121238423Sjhb} 122238423Sjhb 123238423Sjhb# Returns true (0) if a file should be ignored, false otherwise. 124238423Sjhb# 125238423Sjhb# $1 - file pathname 126238423Sjhbignore() 127238423Sjhb{ 128238423Sjhb local pattern - 129238423Sjhb 130238423Sjhb set -o noglob 131238423Sjhb for pattern in $IGNORE_FILES; do 132238423Sjhb set +o noglob 133238423Sjhb case $1 in 134238423Sjhb $pattern) 135238423Sjhb return 0 136238423Sjhb ;; 137238423Sjhb esac 138238423Sjhb set -o noglob 139238423Sjhb done 140238423Sjhb 141238423Sjhb # Ignore /.cshrc and /.profile if they are hardlinked to the 142238423Sjhb # same file in /root. This ensures we only compare those 143238423Sjhb # files once in that case. 144238423Sjhb case $1 in 145238423Sjhb /.cshrc|/.profile) 146238423Sjhb if [ ${DESTDIR}$1 -ef ${DESTDIR}/root$1 ]; then 147238423Sjhb return 0 148238423Sjhb fi 149238423Sjhb ;; 150238423Sjhb *) 151238423Sjhb ;; 152238423Sjhb esac 153238423Sjhb 154238423Sjhb return 1 155238423Sjhb} 156238423Sjhb 157238423Sjhb# Returns true (0) if the new version of a file should always be 158238423Sjhb# installed rather than attempting to do a merge. 159238423Sjhb# 160238423Sjhb# $1 - file pathname 161238423Sjhbalways_install() 162238423Sjhb{ 163238423Sjhb local pattern - 164238423Sjhb 165238423Sjhb set -o noglob 166238423Sjhb for pattern in $ALWAYS_INSTALL; do 167238423Sjhb set +o noglob 168238423Sjhb case $1 in 169238423Sjhb $pattern) 170238423Sjhb return 0 171238423Sjhb ;; 172238423Sjhb esac 173238423Sjhb set -o noglob 174238423Sjhb done 175238423Sjhb 176238423Sjhb return 1 177238423Sjhb} 178238423Sjhb 179238423Sjhb# Build a new tree 180238423Sjhb# 181238423Sjhb# $1 - directory to store new tree in 182238423Sjhbbuild_tree() 183238423Sjhb{ 184238423Sjhb local make 185238423Sjhb 186238423Sjhb make="make $MAKE_OPTIONS" 187238423Sjhb 188238423Sjhb log "Building tree at $1 with $make" 189238423Sjhb mkdir -p $1/usr/obj >&3 2>&1 190238423Sjhb (cd $SRCDIR; $make DESTDIR=$1 distrib-dirs) >&3 2>&1 || return 1 191238423Sjhb 192238423Sjhb if ! [ -n "$nobuild" ]; then 193238423Sjhb (cd $SRCDIR; \ 194238423Sjhb MAKEOBJDIRPREFIX=$1/usr/obj $make _obj SUBDIR_OVERRIDE=etc && 195238423Sjhb MAKEOBJDIRPREFIX=$1/usr/obj $make everything SUBDIR_OVERRIDE=etc && 196238423Sjhb MAKEOBJDIRPREFIX=$1/usr/obj $make DESTDIR=$1 distribution) \ 197238423Sjhb >&3 2>&1 || return 1 198238423Sjhb else 199238423Sjhb (cd $SRCDIR; $make DESTDIR=$1 distribution) >&3 2>&1 || return 1 200238423Sjhb fi 201238423Sjhb chflags -R noschg $1 >&3 2>&1 || return 1 202238423Sjhb rm -rf $1/usr/obj >&3 2>&1 || return 1 203238423Sjhb 204238423Sjhb # Purge auto-generated files. Only the source files need to 205238423Sjhb # be updated after which these files are regenerated. 206238423Sjhb rm -f $1/etc/*.db $1/etc/passwd >&3 2>&1 || return 1 207238423Sjhb 208238423Sjhb # Remove empty files. These just clutter the output of 'diff'. 209238423Sjhb find $1 -type f -size 0 -delete >&3 2>&1 || return 1 210238423Sjhb 211238423Sjhb # Trim empty directories. 212238423Sjhb find -d $1 -type d -empty -delete >&3 2>&1 || return 1 213238423Sjhb return 0 214238423Sjhb} 215238423Sjhb 216238423Sjhb# Generate a new NEWTREE tree. If tarball is set, then the tree is 217238423Sjhb# extracted from the tarball. Otherwise the tree is built from a 218238423Sjhb# source tree. 219238423Sjhbextract_tree() 220238423Sjhb{ 221238423Sjhb # If we have a tarball, extract that into the new directory. 222238423Sjhb if [ -n "$tarball" ]; then 223238423Sjhb if ! (mkdir -p $NEWTREE && tar xf $tarball -C $NEWTREE) \ 224238423Sjhb >&3 2>&1; then 225238423Sjhb echo "Failed to extract new tree." 226238423Sjhb remove_tree $NEWTREE 227238423Sjhb exit 1 228238423Sjhb fi 229238423Sjhb else 230238423Sjhb if ! build_tree $NEWTREE; then 231238423Sjhb echo "Failed to build new tree." 232238423Sjhb remove_tree $NEWTREE 233238423Sjhb exit 1 234238423Sjhb fi 235238423Sjhb fi 236238423Sjhb} 237238423Sjhb 238238423Sjhb# Forcefully remove a tree. Returns true (0) if the operation succeeds. 239238423Sjhb# 240238423Sjhb# $1 - path to tree 241238423Sjhbremove_tree() 242238423Sjhb{ 243238423Sjhb 244238423Sjhb rm -rf $1 >&3 2>&1 245238423Sjhb if [ -e $1 ]; then 246238423Sjhb chflags -R noschg $1 >&3 2>&1 247238423Sjhb rm -rf $1 >&3 2>&1 248238423Sjhb fi 249238423Sjhb [ ! -e $1 ] 250238423Sjhb} 251238423Sjhb 252238423Sjhb# Return values for compare() 253238423SjhbCOMPARE_EQUAL=0 254238423SjhbCOMPARE_ONLYFIRST=1 255238423SjhbCOMPARE_ONLYSECOND=2 256238423SjhbCOMPARE_DIFFTYPE=3 257238423SjhbCOMPARE_DIFFLINKS=4 258238423SjhbCOMPARE_DIFFFILES=5 259238423Sjhb 260238423Sjhb# Compare two files/directories/symlinks. Note that this does not 261238423Sjhb# recurse into subdirectories. Instead, if two nodes are both 262238423Sjhb# directories, they are assumed to be equivalent. 263238423Sjhb# 264238423Sjhb# Returns true (0) if the nodes are identical. If only one of the two 265238423Sjhb# nodes are present, return one of the COMPARE_ONLY* constants. If 266238423Sjhb# the nodes are different, return one of the COMPARE_DIFF* constants 267238423Sjhb# to indicate the type of difference. 268238423Sjhb# 269238423Sjhb# $1 - first node 270238423Sjhb# $2 - second node 271238423Sjhbcompare() 272238423Sjhb{ 273238423Sjhb local first second 274238423Sjhb 275238423Sjhb # If the first node doesn't exist, then check for the second 276238423Sjhb # node. Note that -e will fail for a symbolic link that 277238423Sjhb # points to a missing target. 278238423Sjhb if ! exists $1; then 279238423Sjhb if exists $2; then 280238423Sjhb return $COMPARE_ONLYSECOND 281238423Sjhb else 282238423Sjhb return $COMPARE_EQUAL 283238423Sjhb fi 284238423Sjhb elif ! exists $2; then 285238423Sjhb return $COMPARE_ONLYFIRST 286238423Sjhb fi 287238423Sjhb 288238423Sjhb # If the two nodes are different file types fail. 289238423Sjhb first=`stat -f "%Hp" $1` 290238423Sjhb second=`stat -f "%Hp" $2` 291238423Sjhb if [ "$first" != "$second" ]; then 292238423Sjhb return $COMPARE_DIFFTYPE 293238423Sjhb fi 294238423Sjhb 295238423Sjhb # If both are symlinks, compare the link values. 296238423Sjhb if [ -L $1 ]; then 297238423Sjhb first=`readlink $1` 298238423Sjhb second=`readlink $2` 299238423Sjhb if [ "$first" = "$second" ]; then 300238423Sjhb return $COMPARE_EQUAL 301238423Sjhb else 302238423Sjhb return $COMPARE_DIFFLINKS 303238423Sjhb fi 304238423Sjhb fi 305238423Sjhb 306238423Sjhb # If both are files, compare the file contents. 307238423Sjhb if [ -f $1 ]; then 308238423Sjhb if cmp -s $1 $2; then 309238423Sjhb return $COMPARE_EQUAL 310238423Sjhb else 311238423Sjhb return $COMPARE_DIFFFILES 312238423Sjhb fi 313238423Sjhb fi 314238423Sjhb 315238423Sjhb # As long as the two nodes are the same type of file, consider 316238423Sjhb # them equivalent. 317238423Sjhb return $COMPARE_EQUAL 318238423Sjhb} 319238423Sjhb 320238423Sjhb# Returns true (0) if the only difference between two regular files is a 321238423Sjhb# change in the FreeBSD ID string. 322238423Sjhb# 323238423Sjhb# $1 - path of first file 324238423Sjhb# $2 - path of second file 325238423Sjhbfbsdid_only() 326238423Sjhb{ 327238423Sjhb 328238423Sjhb diff -qI '\$FreeBSD.*\$' $1 $2 >/dev/null 2>&1 329238423Sjhb} 330238423Sjhb 331238423Sjhb# This is a wrapper around compare that will return COMPARE_EQUAL if 332238423Sjhb# the only difference between two regular files is a change in the 333238423Sjhb# FreeBSD ID string. It only makes this adjustment if the -F flag has 334238423Sjhb# been specified. 335238423Sjhb# 336238423Sjhb# $1 - first node 337238423Sjhb# $2 - second node 338238423Sjhbcompare_fbsdid() 339238423Sjhb{ 340238423Sjhb local cmp 341238423Sjhb 342238423Sjhb compare $1 $2 343238423Sjhb cmp=$? 344238423Sjhb 345238423Sjhb if [ -n "$FREEBSD_ID" -a "$cmp" -eq $COMPARE_DIFFFILES ] && \ 346238423Sjhb fbsdid_only $1 $2; then 347238423Sjhb return $COMPARE_EQUAL 348238423Sjhb fi 349238423Sjhb 350238423Sjhb return $cmp 351238423Sjhb} 352238423Sjhb 353238423Sjhb# Returns true (0) if a directory is empty. 354238423Sjhb# 355238423Sjhb# $1 - pathname of the directory to check 356238423Sjhbempty_dir() 357238423Sjhb{ 358238423Sjhb local contents 359238423Sjhb 360238423Sjhb contents=`ls -A $1` 361238423Sjhb [ -z "$contents" ] 362238423Sjhb} 363238423Sjhb 364238423Sjhb# Returns true (0) if one directories contents are a subset of the 365238423Sjhb# other. This will recurse to handle subdirectories and compares 366238423Sjhb# individual files in the trees. Its purpose is to quiet spurious 367238423Sjhb# directory warnings for dryrun invocations. 368238423Sjhb# 369238423Sjhb# $1 - first directory (sub) 370238423Sjhb# $2 - second directory (super) 371238423Sjhbdir_subset() 372238423Sjhb{ 373238423Sjhb local contents file 374238423Sjhb 375238423Sjhb if ! [ -d $1 -a -d $2 ]; then 376238423Sjhb return 1 377238423Sjhb fi 378238423Sjhb 379238423Sjhb # Ignore files that are present in the second directory but not 380238423Sjhb # in the first. 381238423Sjhb contents=`ls -A $1` 382238423Sjhb for file in $contents; do 383238423Sjhb if ! compare $1/$file $2/$file; then 384238423Sjhb return 1 385238423Sjhb fi 386238423Sjhb 387238423Sjhb if [ -d $1/$file ]; then 388238423Sjhb if ! dir_subset $1/$file $2/$file; then 389238423Sjhb return 1 390238423Sjhb fi 391238423Sjhb fi 392238423Sjhb done 393238423Sjhb return 0 394238423Sjhb} 395238423Sjhb 396238423Sjhb# Returns true (0) if a directory in the destination tree is empty. 397238423Sjhb# If this is a dryrun, then this returns true as long as the contents 398238423Sjhb# of the directory are a subset of the contents in the old tree 399238423Sjhb# (meaning that the directory would be empty in a non-dryrun when this 400238423Sjhb# was invoked) to quiet spurious warnings. 401238423Sjhb# 402238423Sjhb# $1 - pathname of the directory to check relative to DESTDIR. 403238423Sjhbempty_destdir() 404238423Sjhb{ 405238423Sjhb 406238423Sjhb if [ -n "$dryrun" ]; then 407238423Sjhb dir_subset $DESTDIR/$1 $OLDTREE/$1 408238423Sjhb return 409238423Sjhb fi 410238423Sjhb 411238423Sjhb empty_dir $DESTDIR/$1 412238423Sjhb} 413238423Sjhb 414238423Sjhb# Output a diff of two directory entries with the same relative name 415238423Sjhb# in different trees. Note that as with compare(), this does not 416238423Sjhb# recurse into subdirectories. If the nodes are identical, nothing is 417238423Sjhb# output. 418238423Sjhb# 419238423Sjhb# $1 - first tree 420238423Sjhb# $2 - second tree 421238423Sjhb# $3 - node name 422238423Sjhb# $4 - label for first tree 423238423Sjhb# $5 - label for second tree 424238423Sjhbdiffnode() 425238423Sjhb{ 426238423Sjhb local first second file old new diffargs 427238423Sjhb 428238423Sjhb if [ -n "$FREEBSD_ID" ]; then 429238423Sjhb diffargs="-I \\\$FreeBSD.*\\\$" 430238423Sjhb else 431238423Sjhb diffargs="" 432238423Sjhb fi 433238423Sjhb 434238423Sjhb compare_fbsdid $1/$3 $2/$3 435238423Sjhb case $? in 436238423Sjhb $COMPARE_EQUAL) 437238423Sjhb ;; 438238423Sjhb $COMPARE_ONLYFIRST) 439238423Sjhb echo 440238423Sjhb echo "Removed: $3" 441238423Sjhb echo 442238423Sjhb ;; 443238423Sjhb $COMPARE_ONLYSECOND) 444238423Sjhb echo 445238423Sjhb echo "Added: $3" 446238423Sjhb echo 447238423Sjhb ;; 448238423Sjhb $COMPARE_DIFFTYPE) 449238423Sjhb first=`file_type $1/$3` 450238423Sjhb second=`file_type $2/$3` 451238423Sjhb echo 452238423Sjhb echo "Node changed from a $first to a $second: $3" 453238423Sjhb echo 454238423Sjhb ;; 455238423Sjhb $COMPARE_DIFFLINKS) 456238423Sjhb first=`readlink $1/$file` 457238423Sjhb second=`readlink $2/$file` 458238423Sjhb echo 459238423Sjhb echo "Link changed: $file" 460238423Sjhb rule "=" 461238423Sjhb echo "-$first" 462238423Sjhb echo "+$second" 463238423Sjhb echo 464238423Sjhb ;; 465238423Sjhb $COMPARE_DIFFFILES) 466238423Sjhb echo "Index: $3" 467238423Sjhb rule "=" 468238423Sjhb diff -u $diffargs -L "$3 ($4)" $1/$3 -L "$3 ($5)" $2/$3 469238423Sjhb ;; 470238423Sjhb esac 471238423Sjhb} 472238423Sjhb 473238423Sjhb# Create missing parent directories of a node in a target tree 474238423Sjhb# preserving the owner, group, and permissions from a specified 475238423Sjhb# template tree. 476238423Sjhb# 477238423Sjhb# $1 - template tree 478238423Sjhb# $2 - target tree 479238423Sjhb# $3 - pathname of the node (relative to both trees) 480238423Sjhbinstall_dirs() 481238423Sjhb{ 482238423Sjhb local args dir 483238423Sjhb 484238423Sjhb dir=`dirname $3` 485238423Sjhb 486238423Sjhb # Nothing to do if the parent directory exists. This also 487238423Sjhb # catches the degenerate cases when the path is just a simple 488238423Sjhb # filename. 489238423Sjhb if [ -d ${2}$dir ]; then 490238423Sjhb return 0 491238423Sjhb fi 492238423Sjhb 493238423Sjhb # If non-directory file exists with the desired directory 494238423Sjhb # name, then fail. 495238423Sjhb if exists ${2}$dir; then 496238423Sjhb # If this is a dryrun and we are installing the 497238423Sjhb # directory in the DESTDIR and the file in the DESTDIR 498238423Sjhb # matches the file in the old tree, then fake success 499238423Sjhb # to quiet spurious warnings. 500238423Sjhb if [ -n "$dryrun" -a "$2" = "$DESTDIR" ]; then 501238423Sjhb if compare $OLDTREE/$dir $DESTDIR/$dir; then 502238423Sjhb return 0 503238423Sjhb fi 504238423Sjhb fi 505238423Sjhb 506238423Sjhb args=`file_type ${2}$dir` 507238423Sjhb warn "Directory mismatch: ${2}$dir ($args)" 508238423Sjhb return 1 509238423Sjhb fi 510238423Sjhb 511238423Sjhb # Ensure the parent directory of the directory is present 512238423Sjhb # first. 513238423Sjhb if ! install_dirs $1 "$2" $dir; then 514238423Sjhb return 1 515238423Sjhb fi 516238423Sjhb 517238423Sjhb # Format attributes from template directory as install(1) 518238423Sjhb # arguments. 519238423Sjhb args=`stat -f "-o %Su -g %Sg -m %0Mp%0Lp" $1/$dir` 520238423Sjhb 521238423Sjhb log "install -d $args ${2}$dir" 522238423Sjhb if [ -z "$dryrun" ]; then 523238423Sjhb install -d $args ${2}$dir >&3 2>&1 524238423Sjhb fi 525238423Sjhb return 0 526238423Sjhb} 527238423Sjhb 528238423Sjhb# Perform post-install fixups for a file. This largely consists of 529238423Sjhb# regenerating any files that depend on the newly installed file. 530238423Sjhb# 531238423Sjhb# $1 - pathname of the updated file (relative to DESTDIR) 532238423Sjhbpost_install_file() 533238423Sjhb{ 534238423Sjhb case $1 in 535238423Sjhb /etc/mail/aliases) 536238423Sjhb # Grr, newaliases only works for an empty DESTDIR. 537238423Sjhb if [ -z "$DESTDIR" ]; then 538238423Sjhb log "newaliases" 539238423Sjhb if [ -z "$dryrun" ]; then 540238423Sjhb newaliases >&3 2>&1 541238423Sjhb fi 542238423Sjhb else 543238423Sjhb NEWALIAS_WARN=yes 544238423Sjhb fi 545238423Sjhb ;; 546238423Sjhb /etc/login.conf) 547238423Sjhb log "cap_mkdb ${DESTDIR}$1" 548238423Sjhb if [ -z "$dryrun" ]; then 549238423Sjhb cap_mkdb ${DESTDIR}$1 >&3 2>&1 550238423Sjhb fi 551238423Sjhb ;; 552238423Sjhb /etc/master.passwd) 553238423Sjhb log "pwd_mkdb -p -d $DESTDIR/etc ${DESTDIR}$1" 554238423Sjhb if [ -z "$dryrun" ]; then 555238423Sjhb pwd_mkdb -p -d $DESTDIR/etc ${DESTDIR}$1 \ 556238423Sjhb >&3 2>&1 557238423Sjhb fi 558238423Sjhb ;; 559238423Sjhb /etc/motd) 560238423Sjhb # /etc/rc.d/motd hardcodes the /etc/motd path. 561238423Sjhb # Don't warn about non-empty DESTDIR's since this 562238423Sjhb # change is only cosmetic anyway. 563238423Sjhb if [ -z "$DESTDIR" ]; then 564238423Sjhb log "sh /etc/rc.d/motd start" 565238423Sjhb if [ -z "$dryrun" ]; then 566238423Sjhb sh /etc/rc.d/motd start >&3 2>&1 567238423Sjhb fi 568238423Sjhb fi 569238423Sjhb ;; 570238423Sjhb esac 571238423Sjhb} 572238423Sjhb 573238423Sjhb# Install the "new" version of a file. Returns true if it succeeds 574238423Sjhb# and false otherwise. 575238423Sjhb# 576238423Sjhb# $1 - pathname of the file to install (relative to DESTDIR) 577238423Sjhbinstall_new() 578238423Sjhb{ 579238423Sjhb 580238423Sjhb if ! install_dirs $NEWTREE "$DESTDIR" $1; then 581238423Sjhb return 1 582238423Sjhb fi 583238423Sjhb log "cp -Rp ${NEWTREE}$1 ${DESTDIR}$1" 584238423Sjhb if [ -z "$dryrun" ]; then 585238423Sjhb cp -Rp ${NEWTREE}$1 ${DESTDIR}$1 >&3 2>&1 586238423Sjhb fi 587238423Sjhb post_install_file $1 588238423Sjhb return 0 589238423Sjhb} 590238423Sjhb 591238423Sjhb# Install the "resolved" version of a file. Returns true if it succeeds 592238423Sjhb# and false otherwise. 593238423Sjhb# 594238423Sjhb# $1 - pathname of the file to install (relative to DESTDIR) 595238423Sjhbinstall_resolved() 596238423Sjhb{ 597238423Sjhb 598238423Sjhb # This should always be present since the file is already 599238423Sjhb # there (it caused a conflict). However, it doesn't hurt to 600238423Sjhb # just be safe. 601238423Sjhb if ! install_dirs $NEWTREE "$DESTDIR" $1; then 602238423Sjhb return 1 603238423Sjhb fi 604238423Sjhb 605238423Sjhb log "cp -Rp ${CONFLICTS}$1 ${DESTDIR}$1" 606238423Sjhb cp -Rp ${CONFLICTS}$1 ${DESTDIR}$1 >&3 2>&1 607238423Sjhb post_install_file $1 608238423Sjhb return 0 609238423Sjhb} 610238423Sjhb 611238423Sjhb# Generate a conflict file when a "new" file conflicts with an 612238423Sjhb# existing file in DESTDIR. 613238423Sjhb# 614238423Sjhb# $1 - pathname of the file that conflicts (relative to DESTDIR) 615238423Sjhbnew_conflict() 616238423Sjhb{ 617238423Sjhb 618238423Sjhb if [ -n "$dryrun" ]; then 619238423Sjhb return 620238423Sjhb fi 621238423Sjhb 622238423Sjhb install_dirs $NEWTREE $CONFLICTS $1 623238423Sjhb diff --changed-group-format='<<<<<<< (local) 624238423Sjhb%<======= 625238423Sjhb%>>>>>>>> (stock) 626238423Sjhb' $DESTDIR/$1 $NEWTREE/$1 > $CONFLICTS/$1 627238423Sjhb} 628238423Sjhb 629238423Sjhb# Remove the "old" version of a file. 630238423Sjhb# 631238423Sjhb# $1 - pathname of the old file to remove (relative to DESTDIR) 632238423Sjhbremove_old() 633238423Sjhb{ 634238423Sjhb log "rm -f ${DESTDIR}$1" 635238423Sjhb if [ -z "$dryrun" ]; then 636238423Sjhb rm -f ${DESTDIR}$1 >&3 2>&1 637238423Sjhb fi 638238423Sjhb echo " D $1" 639238423Sjhb} 640238423Sjhb 641238423Sjhb# Update a file that has no local modifications. 642238423Sjhb# 643238423Sjhb# $1 - pathname of the file to update (relative to DESTDIR) 644238423Sjhbupdate_unmodified() 645238423Sjhb{ 646238423Sjhb local new old 647238423Sjhb 648238423Sjhb # If the old file is a directory, then remove it with rmdir 649238423Sjhb # (this should only happen if the file has changed its type 650238423Sjhb # from a directory to a non-directory). If the directory 651238423Sjhb # isn't empty, then fail. This will be reported as a warning 652238423Sjhb # later. 653238423Sjhb if [ -d $DESTDIR/$1 ]; then 654238423Sjhb if empty_destdir $1; then 655238423Sjhb log "rmdir ${DESTDIR}$1" 656238423Sjhb if [ -z "$dryrun" ]; then 657238423Sjhb rmdir ${DESTDIR}$1 >&3 2>&1 658238423Sjhb fi 659238423Sjhb else 660238423Sjhb return 1 661238423Sjhb fi 662238423Sjhb 663238423Sjhb # If both the old and new files are regular files, leave the 664238423Sjhb # existing file. This avoids breaking hard links for /.cshrc 665238423Sjhb # and /.profile. Otherwise, explicitly remove the old file. 666238423Sjhb elif ! [ -f ${DESTDIR}$1 -a -f ${NEWTREE}$1 ]; then 667238423Sjhb log "rm -f ${DESTDIR}$1" 668238423Sjhb if [ -z "$dryrun" ]; then 669238423Sjhb rm -f ${DESTDIR}$1 >&3 2>&1 670238423Sjhb fi 671238423Sjhb fi 672238423Sjhb 673238423Sjhb # If the new file is a directory, note that the old file has 674238423Sjhb # been removed, but don't do anything else for now. The 675238423Sjhb # directory will be installed if needed when new files within 676238423Sjhb # that directory are installed. 677238423Sjhb if [ -d $NEWTREE/$1 ]; then 678238423Sjhb if empty_dir $NEWTREE/$1; then 679238423Sjhb echo " D $file" 680238423Sjhb else 681238423Sjhb echo " U $file" 682238423Sjhb fi 683238423Sjhb elif install_new $1; then 684238423Sjhb echo " U $file" 685238423Sjhb fi 686238423Sjhb return 0 687238423Sjhb} 688238423Sjhb 689238423Sjhb# Update the FreeBSD ID string in a locally modified file to match the 690238423Sjhb# FreeBSD ID string from the "new" version of the file. 691238423Sjhb# 692238423Sjhb# $1 - pathname of the file to update (relative to DESTDIR) 693238423Sjhbupdate_freebsdid() 694238423Sjhb{ 695238423Sjhb local new dest file 696238423Sjhb 697238423Sjhb # If the FreeBSD ID string is removed from the local file, 698238423Sjhb # there is nothing to do. In this case, treat the file as 699238423Sjhb # updated. Otherwise, if either file has more than one 700238423Sjhb # FreeBSD ID string, just punt and let the user handle the 701238423Sjhb # conflict manually. 702238423Sjhb new=`grep -c '\$FreeBSD.*\$' ${NEWTREE}$1` 703238423Sjhb dest=`grep -c '\$FreeBSD.*\$' ${DESTDIR}$1` 704238423Sjhb if [ "$dest" -eq 0 ]; then 705238423Sjhb return 0 706238423Sjhb fi 707238423Sjhb if [ "$dest" -ne 1 -o "$dest" -ne 1 ]; then 708238423Sjhb return 1 709238423Sjhb fi 710238423Sjhb 711238423Sjhb # If the FreeBSD ID string in the new file matches the FreeBSD ID 712238423Sjhb # string in the local file, there is nothing to do. 713238423Sjhb new=`grep '\$FreeBSD.*\$' ${NEWTREE}$1` 714238423Sjhb dest=`grep '\$FreeBSD.*\$' ${DESTDIR}$1` 715238423Sjhb if [ "$new" = "$dest" ]; then 716238423Sjhb return 0 717238423Sjhb fi 718238423Sjhb 719238423Sjhb # Build the new file in three passes. First, copy all the 720238423Sjhb # lines preceding the FreeBSD ID string from the local version 721238423Sjhb # of the file. Second, append the FreeBSD ID string line from 722238423Sjhb # the new version. Finally, append all the lines after the 723238423Sjhb # FreeBSD ID string from the local version of the file. 724238423Sjhb file=`mktemp $WORKDIR/etcupdate-XXXXXXX` 725238423Sjhb awk '/\$FreeBSD.*\$/ { exit } { print }' ${DESTDIR}$1 >> $file 726238423Sjhb awk '/\$FreeBSD.*\$/ { print }' ${NEWTREE}$1 >> $file 727238423Sjhb awk '/\$FreeBSD.*\$/ { ok = 1; next } { if (ok) print }' \ 728238423Sjhb ${DESTDIR}$1 >> $file 729238423Sjhb 730238423Sjhb # As an extra sanity check, fail the attempt if the updated 731238423Sjhb # version of the file has any differences aside from the 732238423Sjhb # FreeBSD ID string. 733238423Sjhb if ! fbsdid_only ${DESTDIR}$1 $file; then 734238423Sjhb rm -f $file 735238423Sjhb return 1 736238423Sjhb fi 737238423Sjhb 738238423Sjhb log "cp $file ${DESTDIR}$1" 739238423Sjhb if [ -z "$dryrun" ]; then 740238423Sjhb cp $file ${DESTDIR}$1 >&3 2>&1 741238423Sjhb fi 742238423Sjhb rm -f $file 743238423Sjhb post_install_file $1 744238423Sjhb echo " M $1" 745238423Sjhb return 0 746238423Sjhb} 747238423Sjhb 748238423Sjhb# Attempt to update a file that has local modifications. This routine 749238423Sjhb# only handles regular files. If the 3-way merge succeeds without 750238423Sjhb# conflicts, the updated file is installed. If the merge fails, the 751238423Sjhb# merged version with conflict markers is left in the CONFLICTS tree. 752238423Sjhb# 753238423Sjhb# $1 - pathname of the file to merge (relative to DESTDIR) 754238423Sjhbmerge_file() 755238423Sjhb{ 756238423Sjhb local res 757238423Sjhb 758238423Sjhb # Try the merge to see if there is a conflict. 759238423Sjhb merge -q -p ${DESTDIR}$1 ${OLDTREE}$1 ${NEWTREE}$1 >/dev/null 2>&3 760238423Sjhb res=$? 761238423Sjhb case $res in 762238423Sjhb 0) 763238423Sjhb # No conflicts, so just redo the merge to the 764238423Sjhb # real file. 765238423Sjhb log "merge ${DESTDIR}$1 ${OLDTREE}$1 ${NEWTREE}$1" 766238423Sjhb if [ -z "$dryrun" ]; then 767238423Sjhb merge ${DESTDIR}$1 ${OLDTREE}$1 ${NEWTREE}$1 768238423Sjhb fi 769238423Sjhb post_install_file $1 770238423Sjhb echo " M $1" 771238423Sjhb ;; 772238423Sjhb 1) 773238423Sjhb # Conflicts, save a version with conflict markers in 774238423Sjhb # the conflicts directory. 775238423Sjhb if [ -z "$dryrun" ]; then 776238423Sjhb install_dirs $NEWTREE $CONFLICTS $1 777238423Sjhb log "cp -Rp ${DESTDIR}$1 ${CONFLICTS}$1" 778238423Sjhb cp -Rp ${DESTDIR}$1 ${CONFLICTS}$1 >&3 2>&1 779238423Sjhb merge -A -q -L "yours" -L "original" -L "new" \ 780238423Sjhb ${CONFLICTS}$1 ${OLDTREE}$1 ${NEWTREE}$1 781238423Sjhb fi 782238423Sjhb echo " C $1" 783238423Sjhb ;; 784238423Sjhb *) 785238423Sjhb panic "merge failed with status $res" 786238423Sjhb ;; 787238423Sjhb esac 788238423Sjhb} 789238423Sjhb 790238423Sjhb# Returns true if a file contains conflict markers from a merge conflict. 791238423Sjhb# 792238423Sjhb# $1 - pathname of the file to resolve (relative to DESTDIR) 793238423Sjhbhas_conflicts() 794238423Sjhb{ 795238423Sjhb 796238423Sjhb egrep -q '^(<{7}|\|{7}|={7}|>{7}) ' $CONFLICTS/$1 797238423Sjhb} 798238423Sjhb 799238423Sjhb# Attempt to resolve a conflict. The user is prompted to choose an 800238423Sjhb# action for each conflict. If the user edits the file, they are 801238423Sjhb# prompted again for an action. The process is very similar to 802238423Sjhb# resolving conflicts after an update or merge with Perforce or 803238423Sjhb# Subversion. The prompts are modelled on a subset of the available 804238423Sjhb# commands for resolving conflicts with Subversion. 805238423Sjhb# 806238423Sjhb# $1 - pathname of the file to resolve (relative to DESTDIR) 807238423Sjhbresolve_conflict() 808238423Sjhb{ 809238423Sjhb local command junk 810238423Sjhb 811238423Sjhb echo "Resolving conflict in '$1':" 812238423Sjhb edit= 813238423Sjhb while true; do 814238423Sjhb # Only display the resolved command if the file 815238423Sjhb # doesn't contain any conflicts. 816238423Sjhb echo -n "Select: (p) postpone, (df) diff-full, (e) edit," 817238423Sjhb if ! has_conflicts $1; then 818238423Sjhb echo -n " (r) resolved," 819238423Sjhb fi 820238423Sjhb echo 821238423Sjhb echo -n " (h) help for more options: " 822238423Sjhb read command 823238423Sjhb case $command in 824238423Sjhb df) 825238423Sjhb diff -u ${DESTDIR}$1 ${CONFLICTS}$1 826238423Sjhb ;; 827238423Sjhb e) 828238423Sjhb $EDITOR ${CONFLICTS}$1 829238423Sjhb ;; 830238423Sjhb h) 831238423Sjhb cat <<EOF 832238423Sjhb (p) postpone - ignore this conflict for now 833238423Sjhb (df) diff-full - show all changes made to merged file 834238423Sjhb (e) edit - change merged file in an editor 835238423Sjhb (r) resolved - accept merged version of file 836238423Sjhb (mf) mine-full - accept local version of entire file (ignore new changes) 837238423Sjhb (tf) theirs-full - accept new version of entire file (lose local changes) 838238423Sjhb (h) help - show this list 839238423SjhbEOF 840238423Sjhb ;; 841238423Sjhb mf) 842238423Sjhb # For mine-full, just delete the 843238423Sjhb # merged file and leave the local 844238423Sjhb # version of the file as-is. 845238423Sjhb rm ${CONFLICTS}$1 846238423Sjhb return 847238423Sjhb ;; 848238423Sjhb p) 849238423Sjhb return 850238423Sjhb ;; 851238423Sjhb r) 852238423Sjhb # If the merged file has conflict 853238423Sjhb # markers, require confirmation. 854238423Sjhb if has_conflicts $1; then 855238423Sjhb echo "File '$1' still has conflicts," \ 856238423Sjhb "are you sure? (y/n) " 857238423Sjhb read junk 858238423Sjhb if [ "$junk" != "y" ]; then 859238423Sjhb continue 860238423Sjhb fi 861238423Sjhb fi 862238423Sjhb 863238423Sjhb if ! install_resolved $1; then 864238423Sjhb panic "Unable to install merged" \ 865238423Sjhb "version of $1" 866238423Sjhb fi 867238423Sjhb rm ${CONFLICTS}$1 868238423Sjhb return 869238423Sjhb ;; 870238423Sjhb tf) 871238423Sjhb # For theirs-full, install the new 872238423Sjhb # version of the file over top of the 873238423Sjhb # existing file. 874238423Sjhb if ! install_new $1; then 875238423Sjhb panic "Unable to install new" \ 876238423Sjhb "version of $1" 877238423Sjhb fi 878238423Sjhb rm ${CONFLICTS}$1 879238423Sjhb return 880238423Sjhb ;; 881238423Sjhb *) 882238423Sjhb echo "Invalid command." 883238423Sjhb ;; 884238423Sjhb esac 885238423Sjhb done 886238423Sjhb} 887238423Sjhb 888238423Sjhb# Handle a file that has been removed from the new tree. If the file 889238423Sjhb# does not exist in DESTDIR, then there is nothing to do. If the file 890238423Sjhb# exists in DESTDIR and is identical to the old version, remove it 891238423Sjhb# from DESTDIR. Otherwise, whine about the conflict but leave the 892238423Sjhb# file in DESTDIR. To handle directories, this uses two passes. The 893238423Sjhb# first pass handles all non-directory files. The second pass handles 894238423Sjhb# just directories and removes them if they are empty. 895238423Sjhb# 896238423Sjhb# If -F is specified, and the only difference in the file in DESTDIR 897238423Sjhb# is a change in the FreeBSD ID string, then remove the file. 898238423Sjhb# 899238423Sjhb# $1 - pathname of the file (relative to DESTDIR) 900238423Sjhbhandle_removed_file() 901238423Sjhb{ 902238423Sjhb local dest file 903238423Sjhb 904238423Sjhb file=$1 905238423Sjhb if ignore $file; then 906238423Sjhb log "IGNORE: removed file $file" 907238423Sjhb return 908238423Sjhb fi 909238423Sjhb 910238423Sjhb compare_fbsdid $DESTDIR/$file $OLDTREE/$file 911238423Sjhb case $? in 912238423Sjhb $COMPARE_EQUAL) 913238423Sjhb if ! [ -d $DESTDIR/$file ]; then 914238423Sjhb remove_old $file 915238423Sjhb fi 916238423Sjhb ;; 917238423Sjhb $COMPARE_ONLYFIRST) 918238423Sjhb panic "Removed file now missing" 919238423Sjhb ;; 920238423Sjhb $COMPARE_ONLYSECOND) 921238423Sjhb # Already removed, nothing to do. 922238423Sjhb ;; 923238423Sjhb $COMPARE_DIFFTYPE|$COMPARE_DIFFLINKS|$COMPARE_DIFFFILES) 924238423Sjhb dest=`file_type $DESTDIR/$file` 925238423Sjhb warn "Modified $dest remains: $file" 926238423Sjhb ;; 927238423Sjhb esac 928238423Sjhb} 929238423Sjhb 930238423Sjhb# Handle a directory that has been removed from the new tree. Only 931238423Sjhb# remove the directory if it is empty. 932238423Sjhb# 933238423Sjhb# $1 - pathname of the directory (relative to DESTDIR) 934238423Sjhbhandle_removed_directory() 935238423Sjhb{ 936238423Sjhb local dir 937238423Sjhb 938238423Sjhb dir=$1 939238423Sjhb if ignore $dir; then 940238423Sjhb log "IGNORE: removed dir $dir" 941238423Sjhb return 942238423Sjhb fi 943238423Sjhb 944238423Sjhb if [ -d $DESTDIR/$dir -a -d $OLDTREE/$dir ]; then 945238423Sjhb if empty_destdir $dir; then 946238423Sjhb log "rmdir ${DESTDIR}$dir" 947238423Sjhb if [ -z "$dryrun" ]; then 948238423Sjhb rmdir ${DESTDIR}$dir >/dev/null 2>&1 949238423Sjhb fi 950238423Sjhb echo " D $dir" 951238423Sjhb else 952238423Sjhb warn "Non-empty directory remains: $dir" 953238423Sjhb fi 954238423Sjhb fi 955238423Sjhb} 956238423Sjhb 957238423Sjhb# Handle a file that exists in both the old and new trees. If the 958238423Sjhb# file has not changed in the old and new trees, there is nothing to 959238423Sjhb# do. If the file in the destination directory matches the new file, 960238423Sjhb# there is nothing to do. If the file in the destination directory 961238423Sjhb# matches the old file, then the new file should be installed. 962238423Sjhb# Everything else becomes some sort of conflict with more detailed 963238423Sjhb# handling. 964238423Sjhb# 965238423Sjhb# $1 - pathname of the file (relative to DESTDIR) 966238423Sjhbhandle_modified_file() 967238423Sjhb{ 968238423Sjhb local cmp dest file new newdestcmp old 969238423Sjhb 970238423Sjhb file=$1 971238423Sjhb if ignore $file; then 972238423Sjhb log "IGNORE: modified file $file" 973238423Sjhb return 974238423Sjhb fi 975238423Sjhb 976238423Sjhb compare $OLDTREE/$file $NEWTREE/$file 977238423Sjhb cmp=$? 978238423Sjhb if [ $cmp -eq $COMPARE_EQUAL ]; then 979238423Sjhb return 980238423Sjhb fi 981238423Sjhb 982238423Sjhb if [ $cmp -eq $COMPARE_ONLYFIRST -o $cmp -eq $COMPARE_ONLYSECOND ]; then 983238423Sjhb panic "Changed file now missing" 984238423Sjhb fi 985238423Sjhb 986238423Sjhb compare $NEWTREE/$file $DESTDIR/$file 987238423Sjhb newdestcmp=$? 988238423Sjhb if [ $newdestcmp -eq $COMPARE_EQUAL ]; then 989238423Sjhb return 990238423Sjhb fi 991238423Sjhb 992238423Sjhb # If the only change in the new file versus the destination 993238423Sjhb # file is a change in the FreeBSD ID string and -F is 994238423Sjhb # specified, just install the new file. 995238423Sjhb if [ -n "$FREEBSD_ID" -a $newdestcmp -eq $COMPARE_DIFFFILES ] && \ 996238423Sjhb fbsdid_only $NEWTREE/$file $DESTDIR/$file; then 997238423Sjhb if update_unmodified $file; then 998238423Sjhb return 999238423Sjhb else 1000238423Sjhb panic "Updating FreeBSD ID string failed" 1001238423Sjhb fi 1002238423Sjhb fi 1003238423Sjhb 1004238423Sjhb # If the local file is the same as the old file, install the 1005238423Sjhb # new file. If -F is specified and the only local change is 1006238423Sjhb # in the FreeBSD ID string, then install the new file as well. 1007238423Sjhb if compare_fbsdid $OLDTREE/$file $DESTDIR/$file; then 1008238423Sjhb if update_unmodified $file; then 1009238423Sjhb return 1010238423Sjhb fi 1011238423Sjhb fi 1012238423Sjhb 1013238423Sjhb # If the only change in the new file versus the old file is a 1014238423Sjhb # change in the FreeBSD ID string and -F is specified, just 1015238423Sjhb # update the FreeBSD ID string in the local file. 1016238423Sjhb if [ -n "$FREEBSD_ID" -a $cmp -eq $COMPARE_DIFFFILES ] && \ 1017238423Sjhb fbsdid_only $OLDTREE/$file $NEWTREE/$file; then 1018238423Sjhb if update_freebsdid $file; then 1019238423Sjhb continue 1020238423Sjhb fi 1021238423Sjhb fi 1022238423Sjhb 1023238423Sjhb # If the file was removed from the dest tree, just whine. 1024238423Sjhb if [ $newdestcmp -eq $COMPARE_ONLYFIRST ]; then 1025238423Sjhb # If the removed file matches an ALWAYS_INSTALL glob, 1026238423Sjhb # then just install the new version of the file. 1027238423Sjhb if always_install $file; then 1028238423Sjhb log "ALWAYS: adding $file" 1029238423Sjhb if ! [ -d $NEWTREE/$file ]; then 1030238423Sjhb if install_new $file; then 1031238423Sjhb echo " A $file" 1032238423Sjhb fi 1033238423Sjhb fi 1034238423Sjhb return 1035238423Sjhb fi 1036238423Sjhb 1037238423Sjhb case $cmp in 1038238423Sjhb $COMPARE_DIFFTYPE) 1039238423Sjhb old=`file_type $OLDTREE/$file` 1040238423Sjhb new=`file_type $NEWTREE/$file` 1041238423Sjhb warn "Remove mismatch: $file ($old became $new)" 1042238423Sjhb ;; 1043238423Sjhb $COMPARE_DIFFLINKS) 1044238423Sjhb old=`readlink $OLDTREE/$file` 1045238423Sjhb new=`readlink $NEWTREE/$file` 1046238423Sjhb warn \ 1047238423Sjhb "Removed link changed: $file (\"$old\" became \"$new\")" 1048238423Sjhb ;; 1049238423Sjhb $COMPARE_DIFFFILES) 1050238423Sjhb warn "Removed file changed: $file" 1051238423Sjhb ;; 1052238423Sjhb esac 1053238423Sjhb return 1054238423Sjhb fi 1055238423Sjhb 1056238423Sjhb # Treat the file as unmodified and force install of the new 1057238423Sjhb # file if it matches an ALWAYS_INSTALL glob. If the update 1058238423Sjhb # attempt fails, then fall through to the normal case so a 1059238423Sjhb # warning is generated. 1060238423Sjhb if always_install $file; then 1061238423Sjhb log "ALWAYS: updating $file" 1062238423Sjhb if update_unmodified $file; then 1063238423Sjhb return 1064238423Sjhb fi 1065238423Sjhb fi 1066238423Sjhb 1067238423Sjhb # If the file changed types between the old and new trees but 1068238423Sjhb # the files in the new and dest tree are both of the same 1069238423Sjhb # type, treat it like an added file just comparing the new and 1070238423Sjhb # dest files. 1071238423Sjhb if [ $cmp -eq $COMPARE_DIFFTYPE ]; then 1072238423Sjhb case $newdestcmp in 1073238423Sjhb $COMPARE_DIFFLINKS) 1074238423Sjhb new=`readlink $NEWTREE/$file` 1075238423Sjhb dest=`readlink $DESTDIR/$file` 1076238423Sjhb warn \ 1077238423Sjhb "New link conflict: $file (\"$new\" vs \"$dest\")" 1078238423Sjhb return 1079238423Sjhb ;; 1080238423Sjhb $COMPARE_DIFFFILES) 1081238423Sjhb new_conflict $file 1082238423Sjhb echo " C $file" 1083238423Sjhb return 1084238423Sjhb ;; 1085238423Sjhb esac 1086238423Sjhb else 1087238423Sjhb # If the file has not changed types between the old 1088238423Sjhb # and new trees, but it is a different type in 1089238423Sjhb # DESTDIR, then just warn. 1090238423Sjhb if [ $newdestcmp -eq $COMPARE_DIFFTYPE ]; then 1091238423Sjhb new=`file_type $NEWTREE/$file` 1092238423Sjhb dest=`file_type $DESTDIR/$file` 1093238423Sjhb warn "Modified mismatch: $file ($new vs $dest)" 1094238423Sjhb return 1095238423Sjhb fi 1096238423Sjhb fi 1097238423Sjhb 1098238423Sjhb case $cmp in 1099238423Sjhb $COMPARE_DIFFTYPE) 1100238423Sjhb old=`file_type $OLDTREE/$file` 1101238423Sjhb new=`file_type $NEWTREE/$file` 1102238423Sjhb dest=`file_type $DESTDIR/$file` 1103238423Sjhb warn "Modified $dest changed: $file ($old became $new)" 1104238423Sjhb ;; 1105238423Sjhb $COMPARE_DIFFLINKS) 1106238423Sjhb old=`readlink $OLDTREE/$file` 1107238423Sjhb new=`readlink $NEWTREE/$file` 1108238423Sjhb warn \ 1109238423Sjhb "Modified link changed: $file (\"$old\" became \"$new\")" 1110238423Sjhb ;; 1111238423Sjhb $COMPARE_DIFFFILES) 1112238423Sjhb merge_file $file 1113238423Sjhb ;; 1114238423Sjhb esac 1115238423Sjhb} 1116238423Sjhb 1117238423Sjhb# Handle a file that has been added in the new tree. If the file does 1118238423Sjhb# not exist in DESTDIR, simply copy the file into DESTDIR. If the 1119238423Sjhb# file exists in the DESTDIR and is identical to the new version, do 1120238423Sjhb# nothing. Otherwise, generate a diff of the two versions of the file 1121238423Sjhb# and mark it as a conflict. 1122238423Sjhb# 1123238423Sjhb# $1 - pathname of the file (relative to DESTDIR) 1124238423Sjhbhandle_added_file() 1125238423Sjhb{ 1126238423Sjhb local cmp dest file new 1127238423Sjhb 1128238423Sjhb file=$1 1129238423Sjhb if ignore $file; then 1130238423Sjhb log "IGNORE: added file $file" 1131238423Sjhb return 1132238423Sjhb fi 1133238423Sjhb 1134238423Sjhb compare $DESTDIR/$file $NEWTREE/$file 1135238423Sjhb cmp=$? 1136238423Sjhb case $cmp in 1137238423Sjhb $COMPARE_EQUAL) 1138238423Sjhb return 1139238423Sjhb ;; 1140238423Sjhb $COMPARE_ONLYFIRST) 1141238423Sjhb panic "Added file now missing" 1142238423Sjhb ;; 1143238423Sjhb $COMPARE_ONLYSECOND) 1144238423Sjhb # Ignore new directories. They will be 1145238423Sjhb # created as needed when non-directory nodes 1146238423Sjhb # are installed. 1147238423Sjhb if ! [ -d $NEWTREE/$file ]; then 1148238423Sjhb if install_new $file; then 1149238423Sjhb echo " A $file" 1150238423Sjhb fi 1151238423Sjhb fi 1152238423Sjhb return 1153238423Sjhb ;; 1154238423Sjhb esac 1155238423Sjhb 1156238423Sjhb 1157238423Sjhb # Treat the file as unmodified and force install of the new 1158238423Sjhb # file if it matches an ALWAYS_INSTALL glob. If the update 1159238423Sjhb # attempt fails, then fall through to the normal case so a 1160238423Sjhb # warning is generated. 1161238423Sjhb if always_install $file; then 1162238423Sjhb log "ALWAYS: updating $file" 1163238423Sjhb if update_unmodified $file; then 1164238423Sjhb return 1165238423Sjhb fi 1166238423Sjhb fi 1167238423Sjhb 1168238423Sjhb case $cmp in 1169238423Sjhb $COMPARE_DIFFTYPE) 1170238423Sjhb new=`file_type $NEWTREE/$file` 1171238423Sjhb dest=`file_type $DESTDIR/$file` 1172238423Sjhb warn "New file mismatch: $file ($new vs $dest)" 1173238423Sjhb ;; 1174238423Sjhb $COMPARE_DIFFLINKS) 1175238423Sjhb new=`readlink $NEWTREE/$file` 1176238423Sjhb dest=`readlink $DESTDIR/$file` 1177238423Sjhb warn "New link conflict: $file (\"$new\" vs \"$dest\")" 1178238423Sjhb ;; 1179238423Sjhb $COMPARE_DIFFFILES) 1180238423Sjhb # If the only change in the new file versus 1181238423Sjhb # the destination file is a change in the 1182238423Sjhb # FreeBSD ID string and -F is specified, just 1183238423Sjhb # install the new file. 1184238423Sjhb if [ -n "$FREEBSD_ID" ] && \ 1185238423Sjhb fbsdid_only $NEWTREE/$file $DESTDIR/$file; then 1186238423Sjhb if update_unmodified $file; then 1187238423Sjhb return 1188238423Sjhb else 1189238423Sjhb panic \ 1190238423Sjhb "Updating FreeBSD ID string failed" 1191238423Sjhb fi 1192238423Sjhb fi 1193238423Sjhb 1194238423Sjhb new_conflict $file 1195238423Sjhb echo " C $file" 1196238423Sjhb ;; 1197238423Sjhb esac 1198238423Sjhb} 1199238423Sjhb 1200238423Sjhb# Main routines for each command 1201238423Sjhb 1202238423Sjhb# Build a new tree and save it in a tarball. 1203238423Sjhbbuild_cmd() 1204238423Sjhb{ 1205238423Sjhb local dir 1206238423Sjhb 1207238423Sjhb if [ $# -ne 1 ]; then 1208238423Sjhb echo "Missing required tarball." 1209238423Sjhb echo 1210238423Sjhb usage 1211238423Sjhb fi 1212238423Sjhb 1213238423Sjhb log "build command: $1" 1214238423Sjhb 1215238423Sjhb # Create a temporary directory to hold the tree 1216238423Sjhb dir=`mktemp -d $WORKDIR/etcupdate-XXXXXXX` 1217238423Sjhb if [ $? -ne 0 ]; then 1218238423Sjhb echo "Unable to create temporary directory." 1219238423Sjhb exit 1 1220238423Sjhb fi 1221238423Sjhb if ! build_tree $dir; then 1222238423Sjhb echo "Failed to build tree." 1223238423Sjhb remove_tree $dir 1224238423Sjhb exit 1 1225238423Sjhb fi 1226238423Sjhb if ! tar cfj $1 -C $dir . >&3 2>&1; then 1227238423Sjhb echo "Failed to create tarball." 1228238423Sjhb remove_tree $dir 1229238423Sjhb exit 1 1230238423Sjhb fi 1231238423Sjhb remove_tree $dir 1232238423Sjhb} 1233238423Sjhb 1234238423Sjhb# Output a diff comparing the tree at DESTDIR to the current 1235238423Sjhb# unmodified tree. Note that this diff does not include files that 1236238423Sjhb# are present in DESTDIR but not in the unmodified tree. 1237238423Sjhbdiff_cmd() 1238238423Sjhb{ 1239238423Sjhb local file 1240238423Sjhb 1241238423Sjhb if [ $# -ne 0 ]; then 1242238423Sjhb usage 1243238423Sjhb fi 1244238423Sjhb 1245238423Sjhb # Requires an unmodified tree to diff against. 1246238423Sjhb if ! [ -d $NEWTREE ]; then 1247238423Sjhb echo "Reference tree to diff against unavailable." 1248238423Sjhb exit 1 1249238423Sjhb fi 1250238423Sjhb 1251238423Sjhb # Unfortunately, diff alone does not quite provide the right 1252238423Sjhb # level of options that we want, so improvise. 1253238423Sjhb for file in `(cd $NEWTREE; find .) | sed -e 's/^\.//'`; do 1254238423Sjhb if ignore $file; then 1255238423Sjhb continue 1256238423Sjhb fi 1257238423Sjhb 1258238423Sjhb diffnode $NEWTREE "$DESTDIR" $file "stock" "local" 1259238423Sjhb done 1260238423Sjhb} 1261238423Sjhb 1262238423Sjhb# Just extract a new tree into NEWTREE either by building a tree or 1263238423Sjhb# extracting a tarball. This can be used to bootstrap updates by 1264238423Sjhb# initializing the current "stock" tree to match the currently 1265238423Sjhb# installed system. 1266238423Sjhb# 1267238423Sjhb# Unlike 'update', this command does not rotate or preserve an 1268238423Sjhb# existing NEWTREE, it just replaces any existing tree. 1269238423Sjhbextract_cmd() 1270238423Sjhb{ 1271238423Sjhb 1272238423Sjhb if [ $# -ne 0 ]; then 1273238423Sjhb usage 1274238423Sjhb fi 1275238423Sjhb 1276238423Sjhb log "extract command: tarball=$tarball" 1277238423Sjhb 1278238423Sjhb if [ -d $NEWTREE ]; then 1279238423Sjhb if ! remove_tree $NEWTREE; then 1280238423Sjhb echo "Unable to remove current tree." 1281238423Sjhb exit 1 1282238423Sjhb fi 1283238423Sjhb fi 1284238423Sjhb 1285238423Sjhb extract_tree 1286238423Sjhb} 1287238423Sjhb 1288238423Sjhb# Resolve conflicts left from an earlier merge. 1289238423Sjhbresolve_cmd() 1290238423Sjhb{ 1291238423Sjhb local conflicts 1292238423Sjhb 1293238423Sjhb if [ $# -ne 0 ]; then 1294238423Sjhb usage 1295238423Sjhb fi 1296238423Sjhb 1297238423Sjhb if ! [ -d $CONFLICTS ]; then 1298238423Sjhb return 1299238423Sjhb fi 1300238423Sjhb 1301238423Sjhb conflicts=`(cd $CONFLICTS; find . ! -type d) | sed -e 's/^\.//'` 1302238423Sjhb for file in $conflicts; do 1303238423Sjhb resolve_conflict $file 1304238423Sjhb done 1305238423Sjhb 1306238423Sjhb if [ -n "$NEWALIAS_WARN" ]; then 1307238423Sjhb warn "Needs update: /etc/mail/aliases.db" \ 1308238423Sjhb "(requires manual update via newaliases(1))" 1309238423Sjhb echo 1310238423Sjhb echo "Warnings:" 1311238423Sjhb echo " Needs update: /etc/mail/aliases.db" \ 1312238423Sjhb "(requires manual update via newaliases(1))" 1313238423Sjhb fi 1314238423Sjhb} 1315238423Sjhb 1316238423Sjhb# Report a summary of the previous merge. Specifically, list any 1317238423Sjhb# remaining conflicts followed by any warnings from the previous 1318238423Sjhb# update. 1319238423Sjhbstatus_cmd() 1320238423Sjhb{ 1321238423Sjhb 1322238423Sjhb if [ $# -ne 0 ]; then 1323238423Sjhb usage 1324238423Sjhb fi 1325238423Sjhb 1326238423Sjhb if [ -d $CONFLICTS ]; then 1327238423Sjhb (cd $CONFLICTS; find . ! -type d) | sed -e 's/^\./ C /' 1328238423Sjhb fi 1329238423Sjhb if [ -s $WARNINGS ]; then 1330238423Sjhb echo "Warnings:" 1331238423Sjhb cat $WARNINGS 1332238423Sjhb fi 1333238423Sjhb} 1334238423Sjhb 1335238423Sjhb# Perform an actual merge. The new tree can either already exist (if 1336238423Sjhb# rerunning a merge), be extracted from a tarball, or generated from a 1337238423Sjhb# source tree. 1338238423Sjhbupdate_cmd() 1339238423Sjhb{ 1340238423Sjhb local dir 1341238423Sjhb 1342238423Sjhb if [ $# -ne 0 ]; then 1343238423Sjhb usage 1344238423Sjhb fi 1345238423Sjhb 1346238423Sjhb log "update command: rerun=$rerun tarball=$tarball" 1347238423Sjhb 1348238423Sjhb if [ `id -u` -ne 0 ]; then 1349238423Sjhb echo "Must be root to update a tree." 1350238423Sjhb exit 1 1351238423Sjhb fi 1352238423Sjhb 1353238423Sjhb # Enforce a sane umask 1354238423Sjhb umask 022 1355238423Sjhb 1356238423Sjhb # XXX: Should existing conflicts be ignored and removed during 1357238423Sjhb # a rerun? 1358238423Sjhb 1359238423Sjhb # Trim the conflicts tree. Whine if there is anything left. 1360238423Sjhb if [ -e $CONFLICTS ]; then 1361238423Sjhb find -d $CONFLICTS -type d -empty -delete >&3 2>&1 1362238423Sjhb rmdir $CONFLICTS >&3 2>&1 1363238423Sjhb fi 1364238423Sjhb if [ -d $CONFLICTS ]; then 1365238423Sjhb echo "Conflicts remain from previous update, aborting." 1366238423Sjhb exit 1 1367238423Sjhb fi 1368238423Sjhb 1369238423Sjhb if [ -z "$rerun" ]; then 1370238423Sjhb # For a dryrun that is not a rerun, do not rotate the existing 1371238423Sjhb # stock tree. Instead, extract a tree to a temporary directory 1372238423Sjhb # and use that for the comparison. 1373238423Sjhb if [ -n "$dryrun" ]; then 1374238423Sjhb dir=`mktemp -d $WORKDIR/etcupdate-XXXXXXX` 1375238423Sjhb if [ $? -ne 0 ]; then 1376238423Sjhb echo "Unable to create temporary directory." 1377238423Sjhb exit 1 1378238423Sjhb fi 1379238423Sjhb OLDTREE=$NEWTREE 1380238423Sjhb NEWTREE=$dir 1381238423Sjhb 1382238423Sjhb # Rotate the existing stock tree to the old tree. 1383238423Sjhb elif [ -d $NEWTREE ]; then 1384238423Sjhb # First, delete the previous old tree if it exists. 1385238423Sjhb if ! remove_tree $OLDTREE; then 1386238423Sjhb echo "Unable to remove old tree." 1387238423Sjhb exit 1 1388238423Sjhb fi 1389238423Sjhb 1390238423Sjhb # Move the current stock tree. 1391238423Sjhb if ! mv $NEWTREE $OLDTREE >&3 2>&1; then 1392238423Sjhb echo "Unable to rename current stock tree." 1393238423Sjhb exit 1 1394238423Sjhb fi 1395238423Sjhb fi 1396238423Sjhb 1397238423Sjhb if ! [ -d $OLDTREE ]; then 1398238423Sjhb cat <<EOF 1399238423SjhbNo previous tree to compare against, a sane comparison is not possible. 1400238423SjhbEOF 1401238423Sjhb log "No previous tree to compare against." 1402238423Sjhb if [ -n "$dir" ]; then 1403238423Sjhb rmdir $dir 1404238423Sjhb fi 1405238423Sjhb exit 1 1406238423Sjhb fi 1407238423Sjhb 1408238423Sjhb # Populate the new tree. 1409238423Sjhb extract_tree 1410238423Sjhb fi 1411238423Sjhb 1412238423Sjhb # Build lists of nodes in the old and new trees. 1413238423Sjhb (cd $OLDTREE; find .) | sed -e 's/^\.//' | sort > $WORKDIR/old.files 1414238423Sjhb (cd $NEWTREE; find .) | sed -e 's/^\.//' | sort > $WORKDIR/new.files 1415238423Sjhb 1416238423Sjhb # Split the files up into three groups using comm. 1417238423Sjhb comm -23 $WORKDIR/old.files $WORKDIR/new.files > $WORKDIR/removed.files 1418238423Sjhb comm -13 $WORKDIR/old.files $WORKDIR/new.files > $WORKDIR/added.files 1419238423Sjhb comm -12 $WORKDIR/old.files $WORKDIR/new.files > $WORKDIR/both.files 1420238423Sjhb 1421238423Sjhb # Initialize conflicts and warnings handling. 1422238423Sjhb rm -f $WARNINGS 1423238423Sjhb mkdir -p $CONFLICTS 1424238423Sjhb 1425238423Sjhb # The order for the following sections is important. In the 1426238423Sjhb # odd case that a directory is converted into a file, the 1427238423Sjhb # existing subfiles need to be removed if possible before the 1428238423Sjhb # file is converted. Similarly, in the case that a file is 1429238423Sjhb # converted into a directory, the file needs to be converted 1430238423Sjhb # into a directory if possible before the new files are added. 1431238423Sjhb 1432238423Sjhb # First, handle removed files. 1433238423Sjhb for file in `cat $WORKDIR/removed.files`; do 1434238423Sjhb handle_removed_file $file 1435238423Sjhb done 1436238423Sjhb 1437238423Sjhb # For the directory pass, reverse sort the list to effect a 1438238423Sjhb # depth-first traversal. This is needed to ensure that if a 1439238423Sjhb # directory with subdirectories is removed, the entire 1440238423Sjhb # directory is removed if there are no local modifications. 1441238423Sjhb for file in `sort -r $WORKDIR/removed.files`; do 1442238423Sjhb handle_removed_directory $file 1443238423Sjhb done 1444238423Sjhb 1445238423Sjhb # Second, handle files that exist in both the old and new 1446238423Sjhb # trees. 1447238423Sjhb for file in `cat $WORKDIR/both.files`; do 1448238423Sjhb handle_modified_file $file 1449238423Sjhb done 1450238423Sjhb 1451238423Sjhb # Finally, handle newly added files. 1452238423Sjhb for file in `cat $WORKDIR/added.files`; do 1453238423Sjhb handle_added_file $file 1454238423Sjhb done 1455238423Sjhb 1456238423Sjhb if [ -n "$NEWALIAS_WARN" ]; then 1457238423Sjhb warn "Needs update: /etc/mail/aliases.db" \ 1458238423Sjhb "(requires manual update via newaliases(1))" 1459238423Sjhb fi 1460238423Sjhb 1461238423Sjhb if [ -s $WARNINGS ]; then 1462238423Sjhb echo "Warnings:" 1463238423Sjhb cat $WARNINGS 1464238423Sjhb fi 1465238423Sjhb 1466238423Sjhb if [ -n "$dir" ]; then 1467238423Sjhb if [ -z "$dryrun" -o -n "$rerun" ]; then 1468238423Sjhb panic "Should not have a temporary directory" 1469238423Sjhb fi 1470238423Sjhb 1471238423Sjhb remove_tree $dir 1472238423Sjhb fi 1473238423Sjhb} 1474238423Sjhb 1475238423Sjhb# Determine which command we are executing. A command may be 1476238423Sjhb# specified as the first word. If one is not specified then 'update' 1477238423Sjhb# is assumed as the default command. 1478238423Sjhbcommand="update" 1479238423Sjhbif [ $# -gt 0 ]; then 1480238423Sjhb case "$1" in 1481238423Sjhb build|diff|extract|status|resolve) 1482238423Sjhb command="$1" 1483238423Sjhb shift 1484238423Sjhb ;; 1485238423Sjhb -*) 1486238423Sjhb # If first arg is an option, assume the 1487238423Sjhb # default command. 1488238423Sjhb ;; 1489238423Sjhb *) 1490238423Sjhb usage 1491238423Sjhb ;; 1492238423Sjhb esac 1493238423Sjhbfi 1494238423Sjhb 1495238423Sjhb# Set default variable values. 1496238423Sjhb 1497238423Sjhb# The path to the source tree used to build trees. 1498238423SjhbSRCDIR=/usr/src 1499238423Sjhb 1500238423Sjhb# The destination directory where the modified files live. 1501238423SjhbDESTDIR= 1502238423Sjhb 1503238423Sjhb# Ignore changes in the FreeBSD ID string. 1504238423SjhbFREEBSD_ID= 1505238423Sjhb 1506238423Sjhb# Files that should always have the new version of the file installed. 1507238423SjhbALWAYS_INSTALL= 1508238423Sjhb 1509238423Sjhb# Files to ignore and never update during a merge. 1510238423SjhbIGNORE_FILES= 1511238423Sjhb 1512238423Sjhb# Flags to pass to 'make' when building a tree. 1513238423SjhbMAKE_OPTIONS= 1514238423Sjhb 1515238423Sjhb# Include a config file if it exists. Note that command line options 1516238423Sjhb# override any settings in the config file. More details are in the 1517238423Sjhb# manual, but in general the following variables can be set: 1518238423Sjhb# - ALWAYS_INSTALL 1519238423Sjhb# - DESTDIR 1520238423Sjhb# - EDITOR 1521238423Sjhb# - FREEBSD_ID 1522238423Sjhb# - IGNORE_FILES 1523238423Sjhb# - LOGFILE 1524238423Sjhb# - MAKE_OPTIONS 1525238423Sjhb# - SRCDIR 1526238423Sjhb# - WORKDIR 1527238423Sjhbif [ -r /etc/etcupdate.conf ]; then 1528238423Sjhb . /etc/etcupdate.conf 1529238423Sjhbfi 1530238423Sjhb 1531238423Sjhb# Parse command line options 1532238423Sjhbtarball= 1533238423Sjhbrerun= 1534238423Sjhbalways= 1535238423Sjhbdryrun= 1536238423Sjhbignore= 1537238423Sjhbnobuild= 1538238423Sjhbwhile getopts "d:nrs:t:A:BD:FI:L:M:" option; do 1539238423Sjhb case "$option" in 1540238423Sjhb d) 1541238423Sjhb WORKDIR=$OPTARG 1542238423Sjhb ;; 1543238423Sjhb n) 1544238423Sjhb dryrun=YES 1545238423Sjhb ;; 1546238423Sjhb r) 1547238423Sjhb rerun=YES 1548238423Sjhb ;; 1549238423Sjhb s) 1550238423Sjhb SRCDIR=$OPTARG 1551238423Sjhb ;; 1552238423Sjhb t) 1553238423Sjhb tarball=$OPTARG 1554238423Sjhb ;; 1555238423Sjhb A) 1556238423Sjhb # To allow this option to be specified 1557238423Sjhb # multiple times, accumulate command-line 1558238423Sjhb # specified patterns in an 'always' variable 1559238423Sjhb # and use that to overwrite ALWAYS_INSTALL 1560238423Sjhb # after parsing all options. Need to be 1561238423Sjhb # careful here with globbing expansion. 1562238423Sjhb set -o noglob 1563238423Sjhb always="$always $OPTARG" 1564238423Sjhb set +o noglob 1565238423Sjhb ;; 1566238423Sjhb B) 1567238423Sjhb nobuild=YES 1568238423Sjhb ;; 1569238423Sjhb D) 1570238423Sjhb DESTDIR=$OPTARG 1571238423Sjhb ;; 1572238423Sjhb F) 1573238423Sjhb FREEBSD_ID=YES 1574238423Sjhb ;; 1575238423Sjhb I) 1576238423Sjhb # To allow this option to be specified 1577238423Sjhb # multiple times, accumulate command-line 1578238423Sjhb # specified patterns in an 'ignore' variable 1579238423Sjhb # and use that to overwrite IGNORE_FILES after 1580238423Sjhb # parsing all options. Need to be careful 1581238423Sjhb # here with globbing expansion. 1582238423Sjhb set -o noglob 1583238423Sjhb ignore="$ignore $OPTARG" 1584238423Sjhb set +o noglob 1585238423Sjhb ;; 1586238423Sjhb L) 1587238423Sjhb LOGFILE=$OPTARG 1588238423Sjhb ;; 1589238423Sjhb M) 1590238423Sjhb MAKE_OPTIONS="$OPTARG" 1591238423Sjhb ;; 1592238423Sjhb *) 1593238423Sjhb echo 1594238423Sjhb usage 1595238423Sjhb ;; 1596238423Sjhb esac 1597238423Sjhbdone 1598238423Sjhbshift $((OPTIND - 1)) 1599238423Sjhb 1600238423Sjhb# Allow -A command line options to override ALWAYS_INSTALL set from 1601238423Sjhb# the config file. 1602238423Sjhbset -o noglob 1603238423Sjhbif [ -n "$always" ]; then 1604238423Sjhb ALWAYS_INSTALL="$always" 1605238423Sjhbfi 1606238423Sjhb 1607238423Sjhb# Allow -I command line options to override IGNORE_FILES set from the 1608238423Sjhb# config file. 1609238423Sjhbif [ -n "$ignore" ]; then 1610238423Sjhb IGNORE_FILES="$ignore" 1611238423Sjhbfi 1612238423Sjhbset +o noglob 1613238423Sjhb 1614238423Sjhb# Where the "old" and "new" trees are stored. 1615238423SjhbWORKDIR=${WORKDIR:-$DESTDIR/var/db/etcupdate} 1616238423Sjhb 1617238423Sjhb# Log file for verbose output from program that are run. The log file 1618238423Sjhb# is opened on fd '3'. 1619238423SjhbLOGFILE=${LOGFILE:-$WORKDIR/log} 1620238423Sjhb 1621238423Sjhb# The path of the "old" tree 1622238423SjhbOLDTREE=$WORKDIR/old 1623238423Sjhb 1624238423Sjhb# The path of the "new" tree 1625238423SjhbNEWTREE=$WORKDIR/current 1626238423Sjhb 1627238423Sjhb# The path of the "conflicts" tree where files with merge conflicts are saved. 1628238423SjhbCONFLICTS=$WORKDIR/conflicts 1629238423Sjhb 1630238423Sjhb# The path of the "warnings" file that accumulates warning notes from an update. 1631238423SjhbWARNINGS=$WORKDIR/warnings 1632238423Sjhb 1633238423Sjhb# Use $EDITOR for resolving conflicts. If it is not set, default to vi. 1634238423SjhbEDITOR=${EDITOR:-/usr/bin/vi} 1635238423Sjhb 1636238423Sjhb# Handle command-specific argument processing such as complaining 1637238423Sjhb# about unsupported options. Since the configuration file is always 1638238423Sjhb# included, do not complain about extra command line arguments that 1639238423Sjhb# may have been set via the config file rather than the command line. 1640238423Sjhbcase $command in 1641238423Sjhb update) 1642238423Sjhb if [ -n "$rerun" -a -n "$tarball" ]; then 1643238423Sjhb echo "Only one of -r or -t can be specified." 1644238423Sjhb echo 1645238423Sjhb usage 1646238423Sjhb fi 1647238423Sjhb ;; 1648238423Sjhb build|diff|resolve|status) 1649238423Sjhb if [ -n "$dryrun" -o -n "$rerun" -o -n "$tarball" ]; then 1650238423Sjhb usage 1651238423Sjhb fi 1652238423Sjhb ;; 1653238423Sjhb extract) 1654238423Sjhb if [ -n "$dryrun" -o -n "$rerun" ]; then 1655238423Sjhb usage 1656238423Sjhb fi 1657238423Sjhb ;; 1658238423Sjhbesac 1659238423Sjhb 1660238423Sjhb# Open the log file. Don't truncate it if doing a minor operation so 1661238423Sjhb# that a minor operation doesn't lose log info from a major operation. 1662238423Sjhbif ! mkdir -p $WORKDIR 2>/dev/null; then 1663238423Sjhb echo "Failed to create work directory $WORKDIR" 1664238423Sjhbfi 1665238423Sjhb 1666238423Sjhbcase $command in 1667238423Sjhb diff|resolve|status) 1668238423Sjhb exec 3>>$LOGFILE 1669238423Sjhb ;; 1670238423Sjhb *) 1671238423Sjhb exec 3>$LOGFILE 1672238423Sjhb ;; 1673238423Sjhbesac 1674238423Sjhb 1675238423Sjhb${command}_cmd "$@" 1676