1#!/usr/bin/env bash 2# A little script I whipped up to make it easy to 3# patch source trees and have sane error handling 4# -Erik 5# 6# (c) 2002 Erik Andersen <andersen@codepoet.org> 7# 8# Parameters: 9# - "-s", optional. Silent operation, don't print anything if there 10# isn't any error. 11# - the build directory, optional, default value is '.'. The place where are 12# the package sources. 13# - the patch directory, optional, default '../kernel-patches'. The place 14# where are the scripts you want to apply. 15# - other parameters are the patch name patterns, optional, default value is 16# '*'. Pattern(s) describing the patch names you want to apply. 17# 18# The script will look recursively for patches from the patch directory. If a 19# file named 'series' exists then the patches mentioned in it will be applied 20# as plain patches, regardless of their file name. If no 'series' file exists, 21# the script will look for file names matching pattern(s). If the name 22# ends with '.tar.*', '.tbz2' or '.tgz', the file is considered as an archive 23# and will be uncompressed into a directory named 24# '.patches-name_of_the_archive-unpacked'. It's the turn of this directory to 25# be scanned with '*' as pattern. Remember that scanning is recursive. Other 26# files than series file and archives are considered as a patch. 27# 28# Once a patch is found, the script will try to apply it. If its name doesn't 29# end with '.gz', '.bz', '.bz2', '.xz', '.zip', '.Z', '.diff*' or '.patch*', 30# it will be skipped. If necessary, the patch will be uncompressed before being 31# applied. The list of the patches applied is stored in '.applied_patches_list' 32# file in the build directory. 33 34set -e 35 36silent= 37if [ "$1" = "-s" ] ; then 38 # add option to be used by the patch tool 39 silent=-s 40 shift 41fi 42 43# Set directories from arguments, or use defaults. 44builddir=${1-.} 45patchdir=${2-../kernel-patches} 46shift 2 47patchpattern=${@-*} 48 49# use a well defined sorting order 50export LC_COLLATE=C 51 52if [ ! -d "${builddir}" ] ; then 53 echo "Aborting. '${builddir}' is not a directory." 54 exit 1 55fi 56if [ ! -d "${patchdir}" ] ; then 57 echo "Aborting. '${patchdir}' is not a directory." 58 exit 1 59fi 60 61# Remove any rejects present BEFORE patching - Because if there are 62# any, even if patches are well applied, at the end it will complain 63# about rejects in builddir. 64find ${builddir}/ '(' -name '*.rej' -o -name '.*.rej' ')' -print0 | \ 65 xargs -0 -r rm -f 66 67function apply_patch { 68 path="${1%%/}" 69 patch="${2}" 70 case "${path}" in 71 /*) ;; 72 *) path="$PWD/${path}";; 73 esac 74 if [ "$3" ]; then 75 type="series"; uncomp="cat" 76 else 77 case "$patch" in 78 *.gz) 79 type="gzip"; uncomp="gunzip -dc"; ;; 80 *.bz) 81 type="bzip"; uncomp="bunzip -dc"; ;; 82 *.bz2) 83 type="bzip2"; uncomp="bunzip2 -dc"; ;; 84 *.xz) 85 type="xz"; uncomp="unxz -dc"; ;; 86 *.zip) 87 type="zip"; uncomp="unzip -d"; ;; 88 *.Z) 89 type="compress"; uncomp="uncompress -c"; ;; 90 *.diff*) 91 type="diff"; uncomp="cat"; ;; 92 *.patch*) 93 type="patch"; uncomp="cat"; ;; 94 *) 95 echo "Unsupported file type for ${path}/${patch}, skipping"; 96 return 0 97 ;; 98 esac 99 fi 100 if [ -z "$silent" ] ; then 101 echo "" 102 echo "Applying $patch using ${type}: " 103 fi 104 if [ ! -e "${path}/$patch" ] ; then 105 echo "Error: missing patch file ${path}/$patch" 106 exit 1 107 fi 108 existing="$(grep -E "/${patch}\$" ${builddir}/.applied_patches_list || true)" 109 if [ -n "${existing}" ]; then 110 echo "Error: duplicate filename '${patch}'" 111 echo "Conflicting files are:" 112 echo " already applied: ${existing}" 113 echo " to be applied : ${path}/${patch}" 114 exit 1 115 fi 116 echo "${path}/${patch}" >> ${builddir}/.applied_patches_list 117 ${uncomp} "${path}/$patch" | patch -g0 -p1 --no-backup-if-mismatch -d "${builddir}" -t -N $silent 118 if [ $? != 0 ] ; then 119 echo "Patch failed! Please fix ${patch}!" 120 exit 1 121 fi 122} 123 124function scan_patchdir { 125 local path=$1 126 shift 1 127 patches=${@-*} 128 129 # If there is a series file, use it instead of using ls sort order 130 # to apply patches. Skip line starting with a dash. 131 if [ -e "${path}/series" ] ; then 132 # The format of a series file accepts a second field that is 133 # used to specify the number of directory components to strip 134 # when applying the patch, in the form -pN (N an integer >= 0) 135 # We assume this field to always be -p1 whether it is present 136 # or missing. 137 series_patches="`grep -Ev "^#" ${path}/series | cut -d ' ' -f1 2> /dev/null`" 138 for i in $series_patches; do 139 apply_patch "$path" "$i" series 140 done 141 else 142 for i in `cd $path; ls -d $patches 2> /dev/null` ; do 143 if [ -d "${path}/$i" ] ; then 144 scan_patchdir "${path}/$i" 145 elif echo "$i" | grep -q -E "\.tar(\..*)?$|\.tbz2?$|\.tgz$" ; then 146 unpackedarchivedir="$builddir/.patches-$(basename $i)-unpacked" 147 rm -rf "$unpackedarchivedir" 2> /dev/null 148 mkdir "$unpackedarchivedir" 149 ${TAR} -C "$unpackedarchivedir" -xaf "${path}/$i" 150 scan_patchdir "$unpackedarchivedir" 151 else 152 apply_patch "$path" "$i" 153 fi 154 done 155 fi 156} 157 158touch ${builddir}/.applied_patches_list 159scan_patchdir "$patchdir" "$patchpattern" 160 161# Check for rejects... 162if [ "`find $builddir/ '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ] ; then 163 echo "Aborting. Reject files found." 164 exit 1 165fi 166