#! @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_DIR/compat ]
then
export PATH="$QUILT_DIR/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 "^$(quote_bre $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()
{
local patch=$(tail -n 1 $DB 2>/dev/null)
[ -n "$patch" ] && echo "$patch"
}
is_numeric()
{
echo $1 | grep -q '^[0-9]*$'
}
is_applied()
{
local patch=$1
[ -e $DB ] || return 1
grep -q "^$(quote_bre $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 "^$(quote_bre $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
[ $opt_strip_level -eq 0 ] \
&& old_hdr=$new_hdr
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 == "---") && NF > 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 == "---") && NF > 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_DIR/$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