15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/bin/bash -p
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# usage: dirpatcher.sh old_dir patch_dir new_dir
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# dirpatcher creates new_dir from patch_dir by decompressing and copying
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# files, and using goobspatch to apply binary diffs to files in old_dir.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# dirpatcher performs the inverse operation to dirdiffer. For more details,
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# consult dirdiffer.sh.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Exit codes:
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  0  OK
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  1  Unknown failure
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  2  Incorrect number of parameters
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  3  Input directories do not exist or are not directories
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  4  Output directory already exists
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  5  Parent of output directory does not exist or is not a directory
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  6  An input or output directories contains another
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  7  Could not create output directory
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  8  File already exists in output directory
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  9  Found an irregular file (non-directory, file, or symbolic link) in input
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 10  Could not create symbolic link
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 11  Unrecognized file extension
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 12  Attempt to patch a nonexistent or non-regular file
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 13  Patch application failed
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 14  File decompression failed
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 15  File copy failed
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 16  Could not set mode (permissions)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 17  Could not set modification time
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set -eu
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Environment sanitization. Set a known-safe PATH. Clear environment variables
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# that might impact the interpreter's operation. The |bash -p| invocation
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# on the #! line takes the bite out of BASH_ENV, ENV, and SHELLOPTS (among
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# other features), but clearing them here ensures that they won't impact any
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shell scripts used as utility programs. SHELLOPTS is read-only and can't be
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# unset, only unexported.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)export PATH="/usr/bin:/bin:/usr/sbin:/sbin"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)export -n SHELLOPTS
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)shopt -s dotglob nullglob
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# find_tool looks for an executable file named |tool_name|:
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  - in the same directory as this script,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  - if this script is located in a Chromium source tree, at the expected
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#    Release output location in the Mac xcodebuild directory,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  - as above, but in the Debug output location
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If found in any of the above locations, the script's path is output.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Otherwise, this function outputs |tool_name| as a fallback, allowing it to
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# be found (or not) by an ordinary ${PATH} search.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)find_tool() {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local tool_name="${1}"
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local script_dir
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  script_dir="$(dirname "${0}")"
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local tool="${script_dir}/${tool_name}"
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -f "${tool}" ]] && [[ -x "${tool}" ]]; then
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo "${tool}"
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local script_dir_phys
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  script_dir_phys="$(cd "${script_dir}" && pwd -P)"
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${script_dir_phys}" =~ ^(.*)/src/chrome/installer/mac$ ]]; then
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tool="${BASH_REMATCH[1]}/src/xcodebuild/Release/${tool_name}"
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -f "${tool}" ]] && [[ -x "${tool}" ]]; then
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo "${tool}"
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tool="${BASH_REMATCH[1]}/src/xcodebuild/Debug/${tool_name}"
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -f "${tool}" ]] && [[ -x "${tool}" ]]; then
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo "${tool}"
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${tool_name}"
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ME="$(basename "${0}")"
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly ME
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GOOBSPATCH="$(find_tool goobspatch)"
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly GOOBSPATCH
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly BUNZIP2="bunzip2"
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly GUNZIP="gunzip"
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)XZDEC="$(find_tool xzdec)"
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly XZDEC
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly GBS_SUFFIX='$gbs'
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly BZ2_SUFFIX='$bz2'
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly GZ_SUFFIX='$gz'
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly XZ_SUFFIX='$xz'
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly PLAIN_SUFFIX='$raw'
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)err() {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local error="${1}"
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${ME}: ${error}" >& 2
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)declare -a g_cleanup
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cleanup() {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local status=${?}
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap - EXIT
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap '' HUP INT QUIT TERM
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${status} -ge 128 ]]; then
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "Caught signal $((${status} - 128))"
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${#g_cleanup[@]}" -gt 0 ]]; then
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -rf "${g_cleanup[@]}"
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit ${status}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)copy_mode_and_time() {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file="${1}"
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_file="${2}"
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local mode
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mode="$(stat "-f%OMp%OLp" "${patch_file}")"
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! chmod -h "${mode}" "${new_file}"; then
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 16
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ -L "${new_file}" ]]; then
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Symbolic link modification times can't be copied because there's no
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # shell tool that provides direct access to lutimes. Instead, the symbolic
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # link was created with rsync, which already copied the timestamp with
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # lutimes.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! touch -r "${patch_file}" "${new_file}"; then
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 17
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)apply_patch() {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_file="${1}"
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file="${2}"
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_file="${3}"
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patcher="${4}"
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -L "${old_file}" ]] || ! [[ -f "${old_file}" ]]; then
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "can't patch nonexistent or irregular file ${old_file}"
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 12
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! "${patcher}" "${old_file}" "${new_file}" "${patch_file}"; then
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't create ${new_file} by applying ${patch_file} to ${old_file}"
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)decompress_file() {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_file="${1}"
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file="${2}"
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_file="${3}"
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local decompressor="${4}"
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! "${decompressor}" -c < "${patch_file}" > "${new_file}"; then
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't decompress ${patch_file} to ${new_file} with ${decompressor}"
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 14
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)copy_file() {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_file="${1}"
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file="${2}"
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_file="${3}"
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local extra="${4}"
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! cp "${patch_file}" "${new_file}"; then
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 15
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)patch_file() {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_file="${1}"
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file="${2}"
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_file="${3}"
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local operation extra strip_length
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${patch_file: -${#GBS_SUFFIX}}" = "${GBS_SUFFIX}" ]]; then
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operation="apply_patch"
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra="${GOOBSPATCH}"
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strip_length=${#GBS_SUFFIX}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ "${patch_file: -${#BZ2_SUFFIX}}" = "${BZ2_SUFFIX}" ]]; then
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operation="decompress_file"
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra="${BUNZIP2}"
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strip_length=${#BZ2_SUFFIX}
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ "${patch_file: -${#GZ_SUFFIX}}" = "${GZ_SUFFIX}" ]]; then
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operation="decompress_file"
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra="${GUNZIP}"
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strip_length=${#GZ_SUFFIX}
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ "${patch_file: -${#XZ_SUFFIX}}" = "${XZ_SUFFIX}" ]]; then
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operation="decompress_file"
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra="${XZDEC}"
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strip_length=${#XZ_SUFFIX}
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ "${patch_file: -${#PLAIN_SUFFIX}}" = "${PLAIN_SUFFIX}" ]]; then
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operation="copy_file"
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra="patch"
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strip_length=${#PLAIN_SUFFIX}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "don't know how to operate on ${patch_file}"
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 11
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  old_file="${old_file:0:${#old_file} - ${strip_length}}"
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_file="${new_file:0:${#new_file} - ${strip_length}}"
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -e "${new_file}" ]]; then
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "${new_file} already exists"
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 8
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "${operation}" "${old_file}" "${patch_file}" "${new_file}" "${extra}"
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  copy_mode_and_time "${patch_file}" "${new_file}"
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)patch_symlink() {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file="${1}"
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_file="${2}"
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # local target
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # target="$(readlink "${patch_file}")"
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ln -s "${target}" "${new_file}"
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Use rsync instead of the above, as it's the only way to preserve the
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # timestamp of a symbolic link using shell tools.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! rsync -lt "${patch_file}" "${new_file}"; then
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 10
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  copy_mode_and_time "${patch_file}" "${new_file}"
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)patch_dir() {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_dir="${1}"
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_dir="${2}"
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_dir="${3}"
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mkdir "${new_dir}"; then
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 7
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_file
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for patch_file in "${patch_dir}/"*; do
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local file="${patch_file:${#patch_dir} + 1}"
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local old_file="${old_dir}/${file}"
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local new_file="${new_dir}/${file}"
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -e "${new_file}" ]]; then
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "${new_file} already exists"
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 8
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -L "${patch_file}" ]]; then
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      patch_symlink "${patch_file}" "${new_file}"
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif [[ -d "${patch_file}" ]]; then
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      patch_dir "${old_file}" "${patch_file}" "${new_file}"
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif ! [[ -f "${patch_file}" ]]; then
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "can't handle irregular file ${patch_file}"
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 9
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      patch_file "${old_file}" "${patch_file}" "${new_file}"
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  copy_mode_and_time "${patch_dir}" "${new_dir}"
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shell_safe_path ensures that |path| is safe to pass to tools as a
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# command-line argument. If the first character in |path| is "-", "./" is
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# prepended to it. The possibly-modified |path| is output.
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)shell_safe_path() {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local path="${1}"
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${path:0:1}" = "-" ]]; then
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo "./${path}"
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo "${path}"
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)dirs_contained() {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local dir1="${1}/"
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local dir2="${2}/"
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${dir1:0:${#dir2}}" = "${dir2}" ]] ||
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ "${dir2:0:${#dir1}}" = "${dir1}" ]]; then
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)usage() {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "usage: ${ME} old_dir patch_dir new_dir" >& 2
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main() {
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_dir patch_dir new_dir
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  old_dir="$(shell_safe_path "${1}")"
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  patch_dir="$(shell_safe_path "${2}")"
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_dir="$(shell_safe_path "${3}")"
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap cleanup EXIT HUP INT QUIT TERM
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ -d "${old_dir}" ]] || ! [[ -d "${patch_dir}" ]]; then
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "old_dir and patch_dir must exist and be directories"
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 3
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -e "${new_dir}" ]]; then
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "new_dir must not exist"
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 4
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_dir_parent
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_dir_parent="$(dirname "${new_dir}")"
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ -d "${new_dir_parent}" ]]; then
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "new_dir parent directory must exist and be a directory"
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 5
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_dir_phys patch_dir_phys new_dir_parent_phys new_dir_phys
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  old_dir_phys="$(cd "${old_dir}" && pwd -P)"
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  patch_dir_phys="$(cd "${patch_dir}" && pwd -P)"
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_dir_parent_phys="$(cd "${new_dir_parent}" && pwd -P)"
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_dir_phys="${new_dir_parent_phys}/$(basename "${new_dir}")"
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if dirs_contained "${old_dir_phys}" "${patch_dir_phys}" ||
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     dirs_contained "${old_dir_phys}" "${new_dir_phys}" ||
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     dirs_contained "${patch_dir_phys}" "${new_dir_phys}"; then
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "directories must not contain one another"
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 6
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_cleanup+=("${new_dir}")
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  patch_dir "${old_dir}" "${patch_dir}" "${new_dir}"
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unset g_cleanup[${#g_cleanup[@]}]
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap - EXIT
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if [[ ${#} -ne 3 ]]; then
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  usage
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 2
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)fi
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main "${@}"
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)exit ${?}
369