| 1 | #!/bin/bash |
| 2 | # |
| 3 | # rejmerge (pkgutils) |
| 4 | # |
| 5 | # Copyright (c) 2000-2005 Per Liden |
| 6 | # Copyright (c) 2006-2013 by CRUX team (http://crux.nu) |
| 7 | # |
| 8 | # This program is free software; you can redistribute it and/or modify |
| 9 | # it under the terms of the GNU General Public License as published by |
| 10 | # the Free Software Foundation; either version 2 of the License, or |
| 11 | # (at your option) any later version. |
| 12 | # |
| 13 | # This program is distributed in the hope that it will be useful, |
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | # GNU General Public License for more details. |
| 17 | # |
| 18 | # You should have received a copy of the GNU General Public License |
| 19 | # along with this program; if not, write to the Free Software |
| 20 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| 21 | # USA. |
| 22 | # |
| 23 | |
| 24 | info_n() { |
| 25 | echo -n "=======> $1" |
| 26 | } |
| 27 | |
| 28 | info() { |
| 29 | info_n "$1" |
| 30 | echo |
| 31 | } |
| 32 | |
| 33 | interrupted() { |
| 34 | echo "" |
| 35 | info "Aborted." |
| 36 | exit 1 |
| 37 | } |
| 38 | |
| 39 | atexit() { |
| 40 | if [ -e "$TMPFILE" ]; then |
| 41 | rm -f "$TMPFILE" |
| 42 | fi |
| 43 | } |
| 44 | |
| 45 | rejmerge_diff() { |
| 46 | diff -u "$1" "$2" > "$3" |
| 47 | } |
| 48 | |
| 49 | rejmerge_merge() { |
| 50 | diff --old-group-format="%<" \ |
| 51 | --new-group-format="%>" \ |
| 52 | --changed-group-format="<<<<< MERGE CONFLICT $1 >>>>> |
| 53 | %<<<<<< MERGE CONFLICT $2 >>>>> |
| 54 | %><<<<< END MERGE CONFLICT >>>>> |
| 55 | " \ |
| 56 | "$1" "$2" > "$3" |
| 57 | |
| 58 | REJMERGE_MERGE_INFO="$(grep -c '^<<<<< END MERGE CONFLICT >>>>>$' "$3") merge conflict(s)." |
| 59 | } |
| 60 | |
| 61 | permissions_menu() { |
| 62 | while true; do |
| 63 | info "Access permissions $1" |
| 64 | stat -c '%A %U %G %n' "$1" |
| 65 | stat -c '%A %U %G %n' "$2" |
| 66 | while true; do |
| 67 | info_n "[K]eep [U]pgrade [D]iff [S]kip? " |
| 68 | read -n1 CMD |
| 69 | echo |
| 70 | |
| 71 | case "$CMD" in |
| 72 | k|K) chown --reference="$1" "$2" |
| 73 | chmod --reference="$1" "$2" |
| 74 | break 2 |
| 75 | ;; |
| 76 | u|U) chown --reference="$2" "$1" |
| 77 | chmod --reference="$2" "$1" |
| 78 | break 2 |
| 79 | ;; |
| 80 | d|D) break 1 |
| 81 | ;; |
| 82 | s|S) break 2 |
| 83 | ;; |
| 84 | esac |
| 85 | done |
| 86 | done |
| 87 | } |
| 88 | |
| 89 | merge_menu() { |
| 90 | rejmerge_merge "$1" "$2" "$TMPFILE" |
| 91 | |
| 92 | while true; do |
| 93 | info "Merged $1" |
| 94 | cat "$TMPFILE" | more |
| 95 | |
| 96 | if [ "$REJMERGE_MERGE_INFO" ]; then |
| 97 | info "$REJMERGE_MERGE_INFO" |
| 98 | unset REJMERGE_MERGE_INFO |
| 99 | fi |
| 100 | |
| 101 | while true; do |
| 102 | info_n "[I]nstall [E]dit [V]iew [S]kip? " |
| 103 | read -n1 CMD |
| 104 | echo |
| 105 | |
| 106 | case "$CMD" in |
| 107 | i|I) chmod --reference="$1" "$TMPFILE" |
| 108 | mv -f "$TMPFILE" "$1" |
| 109 | rm -f "$2" |
| 110 | break 2 |
| 111 | ;; |
| 112 | e|E) $EDITOR "$TMPFILE" |
| 113 | break 1 |
| 114 | ;; |
| 115 | v|V) break 1 |
| 116 | ;; |
| 117 | s|S) break 2 |
| 118 | ;; |
| 119 | esac |
| 120 | done |
| 121 | done |
| 122 | |
| 123 | : > "$TMPFILE" |
| 124 | } |
| 125 | |
| 126 | diff_menu() { |
| 127 | rejmerge_diff "$1" "$2" "$TMPFILE" |
| 128 | |
| 129 | while true; do |
| 130 | info "$1" |
| 131 | cat "$TMPFILE" | more |
| 132 | while true; do |
| 133 | info_n "[K]eep [U]pgrade [M]erge [D]iff [S]kip? " |
| 134 | read -n1 CMD |
| 135 | echo |
| 136 | |
| 137 | case "$CMD" in |
| 138 | k|K) rm -f "$2" |
| 139 | break 2 |
| 140 | ;; |
| 141 | u|U) mv -f "$2" "$1" |
| 142 | break 2 |
| 143 | ;; |
| 144 | m|M) merge_menu "$1" "$2" |
| 145 | break 2 |
| 146 | ;; |
| 147 | d|D) break 1 |
| 148 | ;; |
| 149 | s|S) break 2 |
| 150 | ;; |
| 151 | esac |
| 152 | done |
| 153 | done |
| 154 | |
| 155 | : > "$TMPFILE" |
| 156 | } |
| 157 | |
| 158 | file_menu() { |
| 159 | while true; do |
| 160 | info "$1" |
| 161 | file "$1" "$2" |
| 162 | while true; do |
| 163 | info_n "[K]eep [U]pgrade [D]iff [S]kip? " |
| 164 | read -n1 CMD |
| 165 | echo |
| 166 | |
| 167 | case "$CMD" in |
| 168 | k|K) rm -f "$2" |
| 169 | break 2 |
| 170 | ;; |
| 171 | u|U) mv -f "$2" "$1" |
| 172 | break 2 |
| 173 | ;; |
| 174 | d|D) break 1 |
| 175 | ;; |
| 176 | s|S) break 2 |
| 177 | ;; |
| 178 | esac |
| 179 | done |
| 180 | done |
| 181 | } |
| 182 | |
| 183 | print_help() { |
| 184 | echo "usage: $REJMERGE_COMMAND [options]" |
| 185 | echo "options:" |
| 186 | echo " -r, --root <path> specify alternative root" |
| 187 | echo " -v, --version print version and exit " |
| 188 | echo " -h, --help print help and exit" |
| 189 | } |
| 190 | |
| 191 | parse_options() { |
| 192 | while [ "$1" ]; do |
| 193 | case $1 in |
| 194 | -r|--root) |
| 195 | if [ ! "$2" ]; then |
| 196 | echo "$REJMERGE_COMMAND: option $1 requires an argument" |
| 197 | exit 1 |
| 198 | fi |
| 199 | REJMERGE_ROOT="$2" |
| 200 | REJMERGE_CONF="$2$REJMERGE_CONF" |
| 201 | REJECTED_DIR="$2$REJECTED_DIR" |
| 202 | shift ;; |
| 203 | -v|--version) |
| 204 | echo "$REJMERGE_COMMAND (pkgutils) $REJMERGE_VERSION" |
| 205 | exit 0 ;; |
| 206 | -h|--help) |
| 207 | print_help |
| 208 | exit 0 ;; |
| 209 | *) |
| 210 | echo "$REJMERGE_COMMAND: invalid option $1" |
| 211 | exit 1 ;; |
| 212 | esac |
| 213 | shift |
| 214 | done |
| 215 | |
| 216 | if [ ! -d "$REJECTED_DIR" ]; then |
| 217 | echo "$REJMERGE_COMMAND: $REJECTED_DIR not found" |
| 218 | exit 1 |
| 219 | fi |
| 220 | } |
| 221 | |
| 222 | files_regular() { |
| 223 | local STAT_FILE1=$(stat -c '%F' "$1") |
| 224 | local STAT_FILE2=$(stat -c '%F' "$2") |
| 225 | |
| 226 | if [ "$STAT_FILE1" != "regular file" ]; then |
| 227 | return 1 |
| 228 | fi |
| 229 | |
| 230 | if [ "$STAT_FILE2" != "regular file" ]; then |
| 231 | return 1 |
| 232 | fi |
| 233 | |
| 234 | return 0 |
| 235 | } |
| 236 | |
| 237 | main() { |
| 238 | parse_options "$@" |
| 239 | |
| 240 | if [ "$UID" != "0" ]; then |
| 241 | echo "$REJMERGE_COMMAND: only root can merge rejected files" |
| 242 | exit 1 |
| 243 | fi |
| 244 | |
| 245 | # Read configuration |
| 246 | if [ -f "$REJMERGE_CONF" ]; then |
| 247 | . "$REJMERGE_CONF" |
| 248 | fi |
| 249 | |
| 250 | REJECTED_FILES_FOUND="no" |
| 251 | |
| 252 | # Check files |
| 253 | for REJECTED_FILE in $(find $REJECTED_DIR ! -type d); do |
| 254 | INSTALLED_FILE="$REJMERGE_ROOT${REJECTED_FILE##$REJECTED_DIR}" |
| 255 | |
| 256 | # Remove rejected file if there is no installed version |
| 257 | if [ ! -e "$INSTALLED_FILE" ]; then |
| 258 | rm -f "$REJECTED_FILE" |
| 259 | continue |
| 260 | fi |
| 261 | |
| 262 | # Check permissions |
| 263 | local STAT_FILE1=$(stat -c '%A %U %G' "$INSTALLED_FILE") |
| 264 | local STAT_FILE2=$(stat -c '%A %U %G' "$REJECTED_FILE") |
| 265 | |
| 266 | if [ "$STAT_FILE1" != "$STAT_FILE2" ]; then |
| 267 | REJECTED_FILES_FOUND="yes" |
| 268 | permissions_menu "$INSTALLED_FILE" "$REJECTED_FILE" |
| 269 | fi |
| 270 | |
| 271 | # Check file types |
| 272 | if files_regular "$INSTALLED_FILE" "$REJECTED_FILE"; then |
| 273 | # Both files are regular |
| 274 | if cmp -s "$INSTALLED_FILE" "$REJECTED_FILE"; then |
| 275 | rm -f "$REJECTED_FILE" |
| 276 | else |
| 277 | REJECTED_FILES_FOUND="yes" |
| 278 | diff_menu "$INSTALLED_FILE" "$REJECTED_FILE" |
| 279 | fi |
| 280 | else |
| 281 | # At least one file is non-regular |
| 282 | REJECTED_FILES_FOUND="yes" |
| 283 | file_menu "$INSTALLED_FILE" "$REJECTED_FILE" |
| 284 | fi |
| 285 | done |
| 286 | |
| 287 | # Remove empty directories |
| 288 | for DIR in $(find $REJECTED_DIR -depth -type d); do |
| 289 | if [ "$DIR" != "$REJECTED_DIR" ]; then |
| 290 | rmdir "$DIR" &> /dev/null |
| 291 | fi |
| 292 | done |
| 293 | |
| 294 | if [ "$REJECTED_FILES_FOUND" = "no" ]; then |
| 295 | echo "Nothing to merge" |
| 296 | fi |
| 297 | |
| 298 | exit 0 |
| 299 | } |
| 300 | |
| 301 | trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM |
| 302 | trap "atexit" EXIT |
| 303 | |
| 304 | export LC_ALL=POSIX |
| 305 | |
| 306 | readonly REJMERGE_VERSION="#VERSION#" |
| 307 | readonly REJMERGE_COMMAND="${0##*/}" |
| 308 | REJMERGE_ROOT="" |
| 309 | REJMERGE_CONF="/etc/rejmerge.conf" |
| 310 | REJECTED_DIR="/var/lib/pkg/rejected" |
| 311 | EDITOR=${EDITOR:-vi} |
| 312 | TMPFILE=$(mktemp) || exit 1 |
| 313 | |
| 314 | main "$@" |
| 315 | |
| 316 | # End of file |