#! @BASH@ # This file contains the common functions used in all quilt script # It is meant to be sourced by bash scripts. # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # See the COPYING and AUTHORS files for more details. export TEXTDOMAIN=quilt export TEXTDOMAINDIR=@LOCALEDIR@ : ${LC_CTYPE:=$LANG} : ${LC_MESSAGES:=$LANG} ORIGINAL_LANG=$LANG export LANG=POSIX export QUILT_PATCHES QUILT_PC SUBDIR SERIES DB DB_VERSION=2 : ${QUILT_PATCHES:=patches} : ${QUILT_PC:=.pc} # Support compatibility layer if [ -d @QUILT@/compat ] then export PATH="@QUILT@/compat:$PATH" fi unset CDPATH shopt -s dotglob if [ -e "$QUILTRC" ] then source "$QUILTRC" fi # Add default arguments for this command if [ -n "$QUILT_COMMAND" ]; then args="QUILT_$(echo $QUILT_COMMAND | tr a-z A-Z)_ARGS" eval set -- ${!args} \"\$@\" unset args fi # ======================================================== #declare -a exit_handlers # #add_exit_handler() { # exit_handlers[${#exit_handlers[@]}]=$1 #} # #remove_exit_handler() { # declare -a handlers # local h # for h in "${exit_handlers[@]}"; do # [ "$h" = "$1" ] && continue # handlers[${#handlers[@]}]=$h # done # exit_handlers=( "${handlers[@]}" ) #} # #run_exit_handlers() { # local h # for h in "${exit_handlers[@]}"; do # eval $h # done #} # #trap run_exit_handlers EXIT # ======================================================== # Quote a string for use in a basic regular expression. quote_bre() { echo "$1" | sed -e 's:\([][^$/.*\\]\):\\\1:g' } # Quote a string for use in an extended regular expression. quote_re() { echo "$1" | sed -e 's:\([][?{(|)}^$/.+*\\]\):\\\1:g' } basename() { local path="${1//\/\//}" path="${path%%/}" echo "${path##*/}" } dirname() { local path="${1//\/\//}" path="${path%%/}" local basename="${path##*/}" path="${path:0:${#path}-${#basename}}" [ x"$path" != x"/" ] && path="${path%/}" echo "${path:-.}" } patch_file_name() { echo "$QUILT_PATCHES/$1" } # The -pN option and possibly others that should be passed to patch. patch_args() { local patch=$1 if [ -e $SERIES ] then awk ' {sub(/(^|[ \t]+)#.*/, "") } $1 == "'"$patch"'" \ { if (NF >= 2) for (i=2; i <= NF; i++) print $i else print "-p1" ; exit } ' $SERIES fi } patch_strip_level() { local patch=$1 i for i in $(patch_args $patch) do case $i in -p) echo $2 return ;; -p*) echo ${i:2} return ;; esac done echo "1" } change_db_strip_level() { local level=$1 patch=$2 if [ x$level != x-p1 ] then level="$level" else level="" fi if [ -e $SERIES ] then local tmpfile=$(gen_tempfile) awk ' /^'"$(quote_re $patch)"'([ \t]|$)/ \ { for(i=2; i<=NF; i++) if ($i ~ /^-p/) { $i="'"$level"'" break } if (i > NF && "'"$level"'" != "") $i="'"$level"'" } { print } ' $SERIES > $tmpfile if ! cmp $SERIES $tmpfile >/dev/null 2>/dev/null then cat $tmpfile > $SERIES fi rm -f $tmpfile else return 1 fi } patch_in_series() { local patch=$1 if ! [ -e $SERIES ] then return 1 else grep -q -E "^$(quote_re $patch)([ \t]|$)" $SERIES fi } # Insert new patch after topmost patch insert_in_series() { local patch=$1 patch_args=$2 local next=$(patches_after "$(top_patch)" | head -n 1) tmpfile if [ -n "$patch_args" ] then patch_args=" $patch_args" fi tmpfile=$(gen_tempfile) || return 1 mkdir -p $(dirname $SERIES) if [ -n "$next" ] then awk ' /^'"$(quote_re $next)"'([ \t]|$)/ \ { print "'"$patch$patch_args"'" } { print } ' $SERIES > $tmpfile status=$? if [ $status -ne 0 ] then rm -f $tmpfile return 1 fi else if [ -e $SERIES ] then cat $SERIES > $tmpfile fi echo "$patch$patch_args" >> $tmpfile fi cat $tmpfile > $SERIES rm -f $tmpfile } remove_from_series() { local patch=$1 tmpfile=$(gen_tempfile) || return 1 awk ' ! /^'"$(quote_re $patch)"'([ \t]|$)/ \ { print } ' $SERIES > $tmpfile if [ $? -ne 0 ] then rm -f $tmpfile return 1 fi cat $tmpfile > $SERIES rm -f $tmpfile } rename_in_series() { local from=$1 to=$2 tmpfile=$(gen_tempfile) || return 1 awk ' /^'"$(quote_re $from)"'([ \t]|$)/ \ { sub(/'"$(quote_re $from)"'/, "'"${to//\"/\\\"}"'") good=1 } { print } END { exit(! good) } ' $SERIES > $tmpfile if [ $? -ne 0 ] then rm -f $tmpfile return 1 fi cat $tmpfile > $SERIES rm -f $tmpfile } rename_in_db() { local from=$1 to=$2 local tmpfile tmpfile=$(gen_tempfile) || return 1 awk ' /^'"$(quote_re $from)"'$/ \ { sub(/'"$(quote_re $from)"'/, "'"${to//\"/\\\"}"'") good=1 } { print } END { exit(! good) } ' $DB > $tmpfile if [ $? -eq 0 ] then cat $tmpfile > $DB rm -f $tmpfile else rm -f $tmpfile return 1 fi } backup_file_name() { local patch=$1 while [ $# -gt 1 ] do echo "$QUILT_PC/$patch/$2" shift done } cat_series() { if [ -e $SERIES ] then sed -e '/^$/d' -e '/^#/d' -e 's/^[ '$'\t'']*//' \ -e 's/[ '$'\t''].*//' $SERIES else return 1 fi } top_patch() { [ -e $DB ] && tail -n 1 $DB } is_numeric() { echo $1 | grep -q '^[0-9]*$' } is_applied() { local patch=$1 [ -e $DB ] || return 1 grep -q -E "^$(quote_re $patch)\$" $DB } applied_patches() { [ -e $DB ] || return 1 cat $DB } applied_before() { local patch=$1 if [ -n "$patch" ] then awk ' $0 == "'"$patch"'" { exit } { print } ' $DB fi } patches_before() { local patch=$1 if [ -n "$patch" ] then cat_series \ | awk ' $0 == "'"$patch"'" { exit } { print } ' fi } patches_after() { local patch=$1 if [ -n "$patch" ] then cat_series \ | awk ' seen { print } $0 == "'"$patch"'" { seen=1 } ' else cat_series fi } # List all patches that have been applied on top of patch $1 patches_on_top_of() { local patch=$1 [ -e $DB ] || return awk ' $0 == "'"$patch"'" { seen=1 ; next } seen { print } ' $DB } # Print the name of the patch that modified the file $2 next after # patch $1, or print nothing if patch $1 is on top. next_patch_for_file() { local patch=$1 file=$2 local patches_on_top=$(patches_on_top_of $patch) if [ -n "$patches_on_top" ] then for patch in $patches_on_top do if [ -f $(backup_file_name $patch $file) ] then echo $patch break fi done fi } add_to_db() { echo $1 >> $DB } remove_from_db() { local patch=$1 local tmpfile if tmpfile=$(gen_tempfile) then grep -v -E "^$(quote_re $patch)\$" $DB > $tmpfile cat $tmpfile > $DB rm -f $tmpfile [ -s $DB ] || rm -f $DB fi } find_patch() { set -- $* if [ $# -eq 1 -a -n "$1" -a -e "$SERIES" ] then local patch="${1#$SUBDIR_DOWN$QUILT_PATCHES/}" local bre=$(quote_bre "$patch") set -- $(sed -e "/^$bre\(\|\.patch\|\.diff\?\)\(\|\.gz\|\.bz2\)\([ "$'\t'"]\|$\)/!d" \ -e 's/[ '$'\t''].*//' $SERIES) if [ $# -eq 1 ] then echo $1 return 0 else # We may have an exact match, which overrides # extension expansion while [ $# -gt 0 ] do if [ $1 = "$patch" ] then echo $1 return 0 fi shift done fi fi return 1 } file_in_patch() { local file=$1 patch=$2 [ -f "$QUILT_PC/$patch/$file" ] } files_in_patch() { local patch="$1" local path="$QUILT_PC/$patch" if [ -d "$path" ] then for file in $(find "$path" -type f \ -a ! -path "$path/.timestamp") do echo "${file:${#path}+1}" done fi } filenames_in_patch() { local patch=$1 local patch_file=$(patch_file_name $patch) if [ -e "$patch_file" ] then local strip=$(patch_strip_level $patch) awk ' ($1 == "+++" || $1 == "---" || $1 == "***") && \ $3 != "----" && $3 != "****" \ { sub(/\t.*/, "") sub(/^... /, "") for (n=0 ; n<'"$strip"'; n++) sub(/^([^/]+\/)/, "") print $0 }' $patch_file fi } files_in_patch_ordered() { local patch=$1 ( files_in_patch $patch | sort echo "-" filenames_in_patch $patch ) | awk ' $1 == "-" { out=1 ; next } !out { files[$0]=1 new_files[++n]=$0 } out { if ($0 in files && !($0 in printed)) { print $0 printed[$0]=1 } } END { for (i=1; i<=n; i++) if (!(new_files[i] in printed)) print new_files[i] } ' } diff_file() { local file=$1 old_file=$2 new_file=$3 local index old_hdr old_date new_hdr new_date line : ${opt_strip_level:=1} if [ $opt_strip_level -eq 0 ] then old_hdr=$file.orig new_hdr=$file else local dir=$(basename $PWD) old_hdr=$dir.orig/$file new_hdr=$dir/$file fi index=$new_hdr if ! [ -s "$old_file" ] then old_file=/dev/null old_hdr=/dev/null [ -n "$QUILT_NO_DIFF_TIMESTAMPS" ] \ || old_date=$'\t'"1970-01-01 00:00:00.000000000 +0000" else [ -n "$QUILT_NO_DIFF_TIMESTAMPS" ] \ || old_date=$'\t'$(date +'%Y-%m-%d %H:%M:%S.%N %z' \ -r "$old_file") fi if ! [ -s "$new_file" ] then new_file=/dev/null new_hdr=/dev/null [ -n "$QUILT_NO_DIFF_TIMESTAMPS" ] \ || new_date=$'\t'"1970-01-01 00:00:00.000000000 +0000" else [ -n "$QUILT_NO_DIFF_TIMESTAMPS" ] \ || new_date=$'\t'$(date +'%Y-%m-%d %H:%M:%S.%N %z' \ -r "$new_file") fi diff -N $QUILT_DIFF_OPTS $old_file $new_file \ --label "$old_hdr$old_date" --label "$new_hdr$new_date" \ | if read line then if [ -z "$QUILT_NO_DIFF_INDEX" ] then echo "Index: $index" echo "===================================================================" fi echo "$line" cat fi } cat_file() { local filename for filename in "$@" do if [ -e "$filename" ] then case "$filename" in *.gz|*.tgz) gzip -cd "$filename" ;; *.bz2) bzip2 -cd "$filename" ;; *) cat "$filename" ;; esac fi done } cat_to_new_file() { local filename="$1" [ -e "$filename" ] && rm -f "$filename" case "$filename" in *.gz) gzip -c ;; *.bz2) bzip2 -c ;; *) cat ;; esac \ > "$filename" } patch_header() { awk ' $1 == "***" || $1 == "---" \ { exit } /^Index:[ \t]|^diff[ \t]|^==*$|^RCS file: |^retrieving revision [0-9]+(\.[0-9]+)*$/ \ { eat = eat $0 "\n" next } { print eat $0 eat = "" } ' } patch_body() { awk ' /^Index:[ \t]|^diff[ \t]|^==*$|^RCS file: |^retrieving revision [0-9]+(\.[0-9]+)*$/ \ { eat = eat $0 "\n" next } $1 == "***" || $1 == "---" \ { body=1 } body { print eat $0 eat = "" next } { eat = "" } ' } in_array() { local a=$1 while [ $# -gt 1 ] do shift [ "$a" = "$1" ] && return 0 done return 1 } gen_tempfile() { local name if [ "$1" = -d ] then mktemp -d ${2:-${TMPDIR:-/tmp}/${0// /_}}.XXXXXX else mktemp ${1:-${TMPDIR:-/tmp}/${0// /_}}.XXXXXX fi } first_modified_by() { local file=$1 patch local -a patches if [ $# -eq 0 ] then patches=( $(applied_patches) ) else shift patches=( "$@" ) fi for patch in ${patches[@]} do if [ -f "$QUILT_PC/$patch/$file" ] then echo $patch return 0 fi done return 1 } create_db() { if ! [ -e $QUILT_PC ] then mkdir -p $QUILT_PC echo $DB_VERSION > $QUILT_PC/.version fi } version_check() { [ -e $QUILT_PC ] || return 0 if [ -e $QUILT_PC/.version ] then version="$(< $QUILT_PC/.version)" if [ "$version" -gt $DB_VERSION ] then printf $"The quilt meta-data in this tree has version %s, but this version of quilt can only handle meta-data formats up to and including version %s. Please pop all the patches using the version of quilt used to push them before downgrading.\n" "$version" "$DB_VERSION" >&2 exit 1 elif [ "$version" = $DB_VERSION ] then return 0 fi fi return 1 } print_patch() { echo -n "${QUILT_PATCHES_PREFIX:+$SUBDIR_DOWN$QUILT_PATCHES/}$1" } setup_colors() { local C=diff_hdr=32:diff_add=36:diff_mod=35:diff_rem=35:diff_hunk=33:diff_ctx=35:diff_cctx=33:patch_offs=33:patch_fuzz=35:patch_fail=31:clear=00 [ -n "$QUILT_COLORS" ] && C="$C:$QUILT_COLORS" C=${C//=/=\'$'\e'[} C=color_${C//:/m\'; color_}m\' eval $C } quilt_command () { local command=$1 shift QUILT_COMMAND="" bash $BASH_OPTS -c ". @QUILT@/$command" "quilt $command" "$@" } # # If the working directory does not contain a $QUILT_PATCHES directory, # quilt searches for its base directory up the directory tree. If no # $QUILT_PATCHES directory exists, the quilt operations that create # patches will create $QUILT_PATCHES in the current working directory. # # When quilt is invoked from a directory below the base directory, it # changes into the base directory, and sets $SUBDIR to the relative # path from the base directory to the directory in which it was # invoked. (e.g., if quilt is invoked in /usr/src/linux/drivers/net # and the base direcory is /usr/src/linux, $SUBDIR is set to # drivers/net/. unset SUBDIR SUBDIR_DOWN if ! [ -d "$QUILT_PATCHES" ] then basedir=$PWD while [ -n "$basedir" ] do basedir=${basedir%/*} down=$down../ if [ -d "$basedir/$QUILT_PATCHES" ] then SUBDIR="${PWD#$basedir/}/" SUBDIR_DOWN=$down if ! cd $basedir/ then echo "Cannot change into parent directory $basedir/" >&2 exit 1 fi break fi done unset basedir down fi if [ -n "$QUILT_SERIES" ] then SERIES=$QUILT_SERIES elif [ -e $QUILT_PC/series ] then SERIES=$QUILT_PC/series elif [ -e series ] then SERIES=series else SERIES=$QUILT_PATCHES/series fi DB="$QUILT_PC/applied-patches" if [ -z "$skip_version_check" ] && ! version_check then printf $"The working tree was created by an older version of quilt. Please run 'quilt upgrade'.\n" >&2 exit 1 fi ### Local Variables: ### mode: shell-script ### End: # vim:filetype=sh