home *** CD-ROM | disk | FTP | other *** search
Wrap
#!/bin/sh # Copyright 2004, 2005 Aaron Madlon-Kay # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ### Variable declaration APPNAME=iPodBackup VERSION=1.5.4 DATE=2005-07-18 EMAIL="ipodbackup@mac.com" HOMEPAGE="http://www.amake.us" #PLATYPUS_ARGS=-o ProgressBar -D #PACKAGE_STYLE=dmg THISAPP="$1/Contents/Resources" CD="$THISAPP/CocoaDialog.app/Contents/MacOS/CocoaDialog" LOGFILE="$HOME/Library/Logs/$APPNAME.log" EXCEPTIONS="$HOME/Library/Preferences/$APPNAME Exceptions.txt" USERPREFS="$HOME/Library/Preferences/$APPNAME Preferences.txt" ### Detects system language, chooses appropriate strings file ### See /System/Library/PrivateFrameworks/IntlPreferences.framework for names get_language() { LANGUAGES=$(defaults read "Apple Global Domain" AppleLanguages | head -n 1 | sed -e 's/[,()"]//g') # Try each language successively until a strings file is found for LANG in $LANGUAGES; do if (echo $LANG | grep -qi "^en"); then STRINGS_FILE="$THISAPP/Localizable.strings" else STRINGS_FILE="$THISAPP/$LANG.lproj/Localizable.strings" fi [[ -r "$STRINGS_FILE" ]] && break done # If the appropriate localized string file isn't found, default to English [[ ! -r "$STRINGS_FILE" ]] && STRINGS_FILE="$THISAPP/Localizable.strings" [[ -r "$STRINGS_FILE" ]] && . "$STRINGS_FILE" echo "Using $STRINGS_LANGUAGE strings version $STRINGS_VERSION by $STRINGS_AUTHOR" >> "$LOGFILE" } ### Reads in all the preferences. load_preferences() { # If preferences or exceptions are missing, copy them in. [[ ! -f "$EXCEPTIONS" ]] && cp "$THISAPP/exceptions.txt" "$EXCEPTIONS" [[ ! -f "$USERPREFS" ]] && cp "$THISAPP/preferences.txt" "$USERPREFS" COMPACT=$(read_pref compact) CHIME=$(read_pref chime) PROMPT=$(read_pref prompt) NO_IPOD_WARNING=$(read_pref no_ipod_warning) DANGER_LEVEL=$(read_pref danger_level) [[ -z "$DANGER_LEVEL" ]] && DANGER_LEVEL=10000000 TOTAL_DATA_SIZE=$(read_pref total_data_size) TOTAL_RUNS=$(read_pref total_runs) DO_NOT_NAG=$(read_pref do_not_nag) PREFERRED_IPOD=$(read_pref preferred_ipod) CHECK_FOR_UPDATES=$(read_pref check_for_updates) OS_VERSION=$(read_pref manual_os_version) OWNERSHIP_WARNING=$(read_pref ownership_warning) [[ -z "$OS_VERSION" ]] && OS_VERSION=$(uname -r | awk -F. '{print $1}') if (( "$OS_VERSION" >= "8" )); then RSYNC="/usr/bin/rsync" ARGS="-avE" else RSYNC="$THISAPP/rsync" ARGS="-av --eahfs" fi IGNORE_ERRORS=$(read_pref ignore_errors) [[ "$IGNORE_ERRORS" = "true" ]] && ARGS="$ARGS --ignore-errors" } ### Reads a specified preference, making sure it is unique read_pref() { if [[ "$#" != "1" ]]; then echo "Error attempting to read pref \"$1\": Bad arguments" >> "$LOGFILE" exit 1 fi if [[ ! -r "$USERPREFS" ]]; then local PREFSFILE="$THISAPP/preferences.txt" else local PREFSFILE="$USERPREFS" fi local READPREF=$(awk -F= "\$1==\"$1\" {print \$2}" "$PREFSFILE") if (( "$(echo "$READPREF" | awk 'END {print NR}')" > "1" )); then echo "Error: Preferences file is invalid: Multiple instances of \"$1\"" >> "$LOGFILE" exit 1 fi echo "$READPREF" } ### Adds setting to prefs file while ensuring it is unique set_pref() { if [[ "$#" != "2" ]]; then echo "Error attempting to set pref \"$1\": Bad arguments" >> "$LOGFILE" exit 1 fi if [[ "$1" = "delete" ]]; then local PREF="$2" else local PREF="$1" local VALUE="$2" fi awk -F= "\$1!=\"$PREF\" {print \$0}" "$USERPREFS" > "$USERPREFS-$$" mv "$USERPREFS-$$" "$USERPREFS" if [[ ! -z "$VALUE" ]]; then echo "$1=$2" >> "$USERPREFS" echo "Set pref \"$1=$2\"" >> "$LOGFILE" fi } ### Make sure user has write permissions on specified item test_permissions() { if [[ ! -w "$1" ]]; then # Bad permissions error "$CD" msgbox --title "$STRING1a" --text "$STRING1b $1" --informative-text "$STRING1c" --style critical --button1 "$STRING1d" > /dev/null echo "Bad permissions on \"$1\"" >> "$LOGFILE" exit 1 fi } ### Deals with arguments passed to this script (drag-and-dropped items) handle_arguments() { if (( "$#" > "1" )); then echo "Editing exceptions via drag-and-drop" >> "$LOGFILE" echo >> "$EXCEPTIONS" until [[ -z "$2" ]]; do edit_exceptions "$2" shift done sed -e '/^$/d' "$EXCEPTIONS" > "$EXCEPTIONS-$$" mv "$EXCEPTIONS-$$" "$EXCEPTIONS" for INDEX in 0 1; do # ${RESULT[0]} is additions, ${RESULT[1]} is removals if [[ -z "${RESULT[$INDEX]}" ]]; then RESULT[$INDEX]="$STRING2f" # "none" else RESULT[$INDEX]=$(echo "${RESULT[$INDEX]}" | sed -e "s/$STRING2g \$//") # Remove trailing punctuation fi done echo "Done editing exceptions" >> "$LOGFILE" # Dialog 2: Editing report "$CD" msgbox --title "$STRING2a" --text "$STRING2b" --informative-text "$STRING2c ${RESULT[0]}$STRING2h $STRING2d ${RESULT[1]}$STRING2h" --button1 "$STRING2e" > /dev/null PROMPT=true fi } ### Add or remove drag-and-dropped items from the exceptions file edit_exceptions() { if (echo "$1" | grep -vq "^$HOME"); then # Dialog 3: Not in home folder error if [[ "$SKIP" != "2" ]]; then SKIP=$("$CD" msgbox --title "$STRING3a" --text "$STRING3b $(basename "$1")" --informative-text "$STRING3c" --style warning --button1 "$STRING3d" --button2 "$STRING3e") fi else FILE=${1:${#HOME}} # Strips $HOME from beginning of path ($1) [[ -d "$1" ]] && FILE=$FILE/ if ([[ -e "$1" ]] && (awk "\$0==\"- $FILE\" {exit 1}" "$EXCEPTIONS")); then echo "Adding $1" >> "$LOGFILE" RESULT[0]=$(echo "${RESULT[0]}$(basename "$1")$STRING2g ") echo -e "- $FILE" >> "$EXCEPTIONS" elif [[ -e "$1" ]]; then echo "Removing $1" >> "$LOGFILE" RESULT[1]=$(echo "${RESULT[1]}$(basename "$1")$STRING2g ") awk "{if (\$0!=\"- $FILE\") print \$0}" "$EXCEPTIONS" > "$EXCEPTIONS-$$" mv "$EXCEPTIONS-$$" "$EXCEPTIONS" else # Dialog 4: Not a valid file error "$CD" msgbox --title "$STRING4a" --text "$STRING4b $(basename "$1")" --informative-text "$STRING4c" --style critical --button1 "$STRING4d" > /dev/null fi fi } ### Test to see if an iPod is connected. If not, quit. test_for_ipod() { IPOD=$(find /Volumes -name "iPod_Control" -maxdepth 2) if (( "$(echo "$IPOD" | awk 'END {print NR}')" > "1" )); then # If the preferred iPod isn't found among current iPods, ask user if (echo "$IPOD" | awk "\$0==\"$PREFERRED_IPOD\" {exit 1}"); then # Dialog 5: Multiple iPods warning echo -n "Multiple iPods found ... " >> "$LOGFILE" ASK=($("$CD" dropdown --title "$STRING5a" --text "$STRING5b" --items $(echo "$IPOD" | awk -F/ '$2=="Volumes" {print $3}' | tr " " "_" | tr "\n" " ") --button1 "$STRING5c" --button2 "$STRING5d" --button3 "$STRING5e" --no-newline --width 400)) case "${ASK[0]}" in "2") echo "User cancelled the iPod selection" >> "$LOGFILE" exit 1;; "1") IPOD=$(echo "$IPOD" | sed -n "$((${ASK[1]} + 1))p") echo "User chose \"$IPOD\"" >> "$LOGFILE";; "3") IPOD=$(echo "$IPOD" | sed -n "$((${ASK[1]} + 1))p") echo "User prefers \"$IPOD\"" >> "$LOGFILE" set_pref preferred_ipod "$IPOD";; esac else IPOD="$PREFERRED_IPOD" fi fi if [[ ! -d "$IPOD" ]]; then echo "No iPod was found!" >> "$LOGFILE" if [[ "$NO_IPOD_WARNING" != "false" ]]; then # Dialog 6: No iPod was found ASK=$("$CD" msgbox --title "$STRING6a" --text "$STRING6b" --style critical --button1 "$STRING6c" --button2 "$STRING6d" --no-newline) case "$ASK" in "1") ;; "2") set_pref no_ipod_warning false esac fi exit 1 fi IPODNAME=$(echo "$IPOD" | awk -F/ '{print $3}') DESTINATION="$IPOD"/../Users/"$USER" DISKIMAGE="$DESTINATION".sparseimage } ### Set up backup and prompt the user to set preferences. first_time_setup() { echo "No previous backup detected" >> "$LOGFILE" test_permissions "$IPOD"/../ mkdir -m 777 "$IPOD"/../Users 2> /dev/null test_permissions "$IPOD"/../Users touch "$IPOD"/../Users/.localized # Dialog 7: Welcome ASK=$("$CD" msgbox --title "$STRING7a" --text "$STRING7b" --informative-text "$STRING7c" --button1 "$STRING7d" --button2 "$STRING7e" --button3 "$STRING7f" --no-newline) case "$ASK" in "1") ;; "2") echo "User cancelled the setup" >> "$LOGFILE" exit 0;; "3") open "$HOMEPAGE/software/$(echo $APPNAME | tr "[:upper:]" "[:lower:]")";; esac # Dialog 8: Logging ASK=$("$CD" msgbox --title "$STRING8a" --text "$STRING8b" --informative-text "$STRING8c" --button1 "$STRING8d" --button2 "$STRING8e" --no-newline) case "$ASK" in "1") ;; "2") open "$LOGFILE";; esac if (( "$OS_VERSION" <= "6" )); then # Sparse disk images are not supported on OS X 10.2 and earlier create_destination 3 else # Dialog 9: Backup type ASK=$("$CD" msgbox --title "$STRING9a" --text "$STRING9b" --informative-text "$STRING9c" --button1 "$STRING9d" --button2 "$STRING9e" --button3 "$STRING9f" --no-newline) create_destination $ASK fi # Dialog 10: "Customize or Default" ASK=$("$CD" msgbox --title "$STRING10a" --text "$STRING10b" --informative-text "$STRING10c" --button1 "$STRING10d" --button2 "$STRING10e" --button3 "$STRING10f" --no-newline) case "$ASK" in "1") customize_settings;; "2") cp "$THISAPP"/preferences.txt "$USERPREFS";; "3") ;; esac # Dialog 11: Disk space warning "$CD" msgbox --style warning --title "$STRING11a" --text "$STRING11b" --informative-text "$STRING11c" --button1 "$STRING11d" > /dev/null } ### Set additional preferences customize_settings() { echo "Customizing preferences" >> "$LOGFILE" # Dialog 12: Exceptions ASK=$("$CD" msgbox --title "$STRING12a" --text "$STRING12b" --informative-text "$STRING12c" --button1 "$STRING12d" --button2 "$STRING12e" --no-newline) case "$ASK" in "1") ;; "2") open "$EXCEPTIONS" # Dialog 13: Waiting for rule edit "$CD" msgbox --title "$STRING13a" --text "$STRING13b" --button1 "$STRING13c" > /dev/null;; esac if [[ -f "$DISKIMAGE" ]]; then # Dialog 14: Compacting ASK=$("$CD" msgbox --title "$STRING14a" --text "$STRING14b" --informative-text "$STRING14c" --button1 "$STRING14d" --button2 "$STRING14e" --no-newline) case "$ASK" in "1") set_pref compact true;; "2") set_pref compact false;; esac fi # Dialog 15: Chime ASK=$("$CD" msgbox --title "$STRING15a" --text "$STRING15b" --informative-text "$STRING15c" --button1 "$STRING15d" --button2 "$STRING15e" --no-newline) case "$ASK" in "1") # Dialog 16: Chime select set_pref chime "$("$CD" fileselect --title "$STRING16a" --text "$STRING16b" --with-extensions .snd .wav .aiff .aif --with-directory /System/Library/Sounds --no-newline)";; "2") set_pref chime none;; esac # Dialog 17: Prompt dialog ASK=$("$CD" msgbox --title "$STRING17a" --text "$STRING17b" --informative-text "$STRING17c" --button1 "$STRING17d" --button2 "$STRING17e" --no-newline) case "$ASK" in "1") set_pref prompt false;; "2") set_pref prompt true;; esac } ### Displays prompt, if enabled. User is given option of editing rules. preflight_prompt() { if [[ "$PROMPT" = "true" ]]; then echo "Displaying preflight prompt" >> "$LOGFILE" # Dialog 18: Preflight prompt ASK=$("$CD" msgbox --title "$STRING18a" --text "$STRING18b" --button1 "$STRING18c" --button2 "$STRING18d" --button3 "$STRING18e" --no-newline) case "$ASK" in "1") ;; "2") echo "User cancelled the backup" >> "$LOGFILE" exit 0;; "3") open "$EXCEPTIONS" # Dialog 13: Waiting for rule edit "$CD" msgbox --title "$STRING13a" --text "$STRING13b" --button1 "$STRING13c" > /dev/null;; esac fi } ### Creates the folder or disk image, then double-checks it. create_destination() { case "$1" in "1") echo "Selection: encrypted disk image" >> "$LOGFILE" ENCRYPTION="-encryption";; "2") echo "Selection: non-encrypted disk image" >> "$LOGFILE" ENCRYPTION="";; "3") echo "Selection: folder" >> "$LOGFILE" echo "Creating folder \"$DESTINATION\"" >> "$LOGFILE" mkdir "$DESTINATION" if [[ ! -d "$DESTINATION" ]]; then # Dialog 19: Folder creation error "$CD" msgbox --title "$STRING19a" --text "$STRING19b" --informative-text "$STRING19c" --style critical --button1 "$STRING19d" > /dev/null exit 1 fi return;; esac echo "Creating disk image \"$DISKIMAGE\"" >> "$LOGFILE" hdiutil create -quiet $ENCRYPTION -fs HFS+J -type SPARSE -size 60g -volname "$USER Backup" "$DISKIMAGE" >> "$LOGFILE" if [[ ! -f "$DISKIMAGE" ]]; then # Dialog 20: Disk image creation error "$CD" msgbox --title "$STRING20a" --text "$STRING20b" --informative-text "$STRING20c" --style critical --button1 "$STRING20d" > /dev/null exit 1 fi } ### Mounts the disk image, if present, then checks the destination to see if ### ownership is enabled. prepare_destination() { if [[ -d "$DESTINATION" && ! -e "$DISKIMAGE" ]]; then echo "Not using a disk image" >> "$LOGFILE" else echo "Using disk image \"$DISKIMAGE\"" >> "$LOGFILE" DESTINATION="/Volumes/$USER Backup" DEVICE=$(hdiutil attach "$DISKIMAGE" | awk "\$0~/$USER Backup/ {print \$1}") if ([[ ! -d "$DESTINATION" ]] || [[ -d "$DESTINATION 1" ]]); then echo "Error mounting disk image. Contents of /Volumes:" >> "$LOGFILE" echo "$(ls -al /Volumes/)" >> "$LOGFILE" # Dialog 21: Disk image mounting error "$CD" msgbox --title "$STRING21a" --text "$STRING21b" --style critical --button1 "$STRING21c" > /dev/null bug_report exit 1 fi fi test_permissions "$DESTINATION" ! (vsdbutil -c "$DESTINATION" | grep -q "enabled") && enable_ownership } # Jump through hoops to properly pass UTF-16 disk name to AppleScript enable_ownership() { echo "$DESTINATION" | awk -F/ '{print $3}' | iconv -f UTF-8 -t UTF-16 | tr -d '\n' > /tmp/iPodBackup-temp$$ osascript -e "set MYDISK to read POSIX file \"/tmp/iPodBackup-temp$$\" as Unicode text" -e "tell application \"Finder\" to set ignore privileges of disk MYDISK to false" > /dev/null 2>> "$LOGFILE" if ([[ "$?" = "1" ]] && ([[ "$OWNERSHIP_WARNING" != "false" ]] && (vsdbutil -c "$DESTINATION" | grep -q "disabled"))); then # Dialog 22: Ownership error ASK=$("$CD" msgbox --title "$STRING22a" --text "$STRING22b" --informative-text "$STRING22c" --button1 "$STRING22d" --button2 "$STRING22e" --no-newline) case "$ASK" in "1") ;; "2") set_pref ownership_warning false;; esac fi } ### Calls rsync to do the dirty work, then makes sure the log doesn't explode. execute_backup() { echo "Backing up \"$HOME\" to \"$DESTINATION\" using \"$RSYNC $ARGS\"" >> "$LOGFILE" if (( "$OS_VERSION" <= "7" )); then # Execute sync in background and monitor logfile for explosion "$RSYNC" $ARGS --exclude-from "$EXCEPTIONS" "$HOME"/ "$DESTINATION" --delete >> "$LOGFILE" 2>> "$LOGFILE" & sleep 5 while (ps -ww -p $! | grep -q "$RSYNC"); do LOGSIZE=$(ls -l "$LOGFILE" | awk '{print $5}') if (( "$LOGSIZE" > "$DANGER_LEVEL" )); then killall -u "$USER" -STOP rsync echo "Caught rsync misbehaving with log at $LOGSIZE bytes" >> "$LOGFILE" # Dialog 23: Exploding log warning ASK=$("$CD" msgbox --title "$STRING23a" --text "$STRING23b" --informative-text "$STRING23c" --style critical --button1 "$STRING23d" --button2 "$STRING23e" --no-newline) case "$ASK" in "1") killall -u "$USER" -KILL rsync return 1;; "2") echo "rsync given one more chance" >> "$LOGFILE" (( DANGER_LEVEL *= 2 )) killall -u "$USER" -CONT rsync;; esac fi sleep 5 done # Scrape exit code from logfile ($? doesn't work for background procs) EXIT_CODE=$(awk -F"code " '$0~/^rsync.*\(code/ {print $2}' "$LOGFILE" | awk -F")" '{print $1}' | head -n 1) [[ -z "$EXIT_CODE" ]] && EXIT_CODE=0 else "$RSYNC" $ARGS --exclude-from "$EXCEPTIONS" "$HOME"/ "$DESTINATION" --delete >> "$LOGFILE" 2>> "$LOGFILE" EXIT_CODE="$?" fi return "$EXIT_CODE" } ### Deals with failure on the part of execute_backup error_handling() { case "$1" in "0" | "23") echo "Backup finished without major errors (exit code $1)" >> "$LOGFILE" donation_nag;; "24") echo "Backup finished with minor errors (exit code $1)" >> "$LOGFILE" # Dialog 29: IO error occurred ASK=$("$CD" msgbox --title "$STRING29a" --text "$STRING29b" --informative-text "$STRING29c" --style critical --button1 "$STRING29d" --button2 "$STRING29e" --no-newline) case "$ASK" in "1") ;; "2") set_pref ignore_errors true;; esac donation_nag;; "12") if ( (grep -q "No space left on device" "$LOGFILE") || (( "$OS_VERSION" <= "7" ))); then echo "Backup did not finish, possibly because destination is full (exit code $1)" >> "$LOGFILE" # Dialog 30: Destination full? ASK=$("$CD" msgbox --title "$STRING30a" --text "$STRING30b" --informative-text "$STRING30c" --style critical --button1 "$STRING30d" --no-newline) elif (( "$OS_VERSION" >= "8" )); then echo "An error occurred: rsync exited with code $1" >> "$LOGFILE" tiger_bug_report fi;; "138") if (( "$OS_VERSION" >= "8" )); then echo "An error occurred: rsync exited with code $1" >> "$LOGFILE" tiger_bug_report else echo "An error occurred: rsync exited with code $1" >> "$LOGFILE" bug_report fi;; *) echo "An error occurred: rsync exited with code $1" >> "$LOGFILE" bug_report esac cp "$LOGFILE" "$DESTINATION" } ### Prepares and offers to send a bug report bug_report() { COMPACT=false CHIME=false if (( "$(awk 'END {print NR}' "$LOGFILE")" > "100" )); then TEMP_LOG=/tmp/"$APPNAME Bug Report $$.log" head -n 50 "$LOGFILE" > "$TEMP_LOG" echo "(...)" >> "$TEMP_LOG" tail -n 50 "$LOGFILE" >> "$TEMP_LOG" else TEMP_LOG="$LOGFILE" fi open "$TEMP_LOG" # Dialog 24: Send bug report ASK=$("$CD" msgbox --title "$STRING24a" --text "$STRING24b" --informative-text "$STRING24c" --style critical --button1 "$STRING24d" --button2 "$STRING24e" --no-newline) case "$ASK" in "1") ;; "2") SUBJECT="$APPNAME $VERSION ($DATE) bug report" osascript -e "tell application \"Mail\"" \ -e "set NEW_MESSAGE to make new outgoing message with properties {subject:\"$SUBJECT\", content:\"Bug report\n\"}" \ -e "tell NEW_MESSAGE" \ -e "set visible to true" \ -e "make new to recipient at beginning of to recipients with properties {name:\"$EMAIL\", address:\"$EMAIL\"}" \ -e "tell content" \ -e "make new attachment with properties {file name:\"$TEMP_LOG\"} at after the last word of the last paragraph" \ -e "end tell" \ -e "end tell" \ -e "end tell";; esac } ### Bitches about Tiger rsync bugs and then calls bug_report tiger_bug_report() { # Dialog 27: Tiger rsync bug ASK=$("$CD" msgbox --title "$STRING27a" --text "$STRING27b" --informative-text "$STRING27c" --style critical --button1 "$STRING27d" --button2 "$STRING27e" --no-newline) case "$ASK" in "1") ;; "2") set_pref manual_os_version 7;; esac bug_report } ### Unmounts the disk image if one was used. Also compacts, if enabled. unmount_diskimage() { if [[ -d "/Volumes/$USER Backup" ]]; then hdiutil detach -quiet "$DEVICE" echo "Disk image ejected" >> "$LOGFILE" # Compact or not? if [[ "$COMPACT" = "true" ]]; then echo -n "Compacting disk image ... " >> "$LOGFILE" hdiutil compact "$DISKIMAGE" -quiet >> "$LOGFILE" echo "done" >> "$LOGFILE" fi fi } ### Plays chime, if enabled. postflight_chime() { if [[ "$CHIME" != "none" && -f "$CHIME" ]]; then echo "Playing chime \"$CHIME\"" >> "$LOGFILE" "$THISAPP"/sndplay "$CHIME" fi } ### Sends alert to Growl send_growl_notification() { if (ps -axww | grep "GrowlHelperApp" | grep -qv "grep"); then "$THISAPP"/growlnotify -n "$APPNAME" -a "$APPNAME" "$APPNAME" -m "$1" fi } ### Nags about giving a donation. What can I say? I'm poor. donation_nag() { DATA_SIZE=$(awk '($1=="sent" || $1=="wrote") && $3=="bytes" {print $2}' "$LOGFILE") [[ ! -z "$DATA_SIZE" ]] && (( TOTAL_DATA_SIZE+="$DATA_SIZE" )) (( TOTAL_RUNS++ )) set_pref total_data_size "$TOTAL_DATA_SIZE" set_pref total_runs "$TOTAL_RUNS" if ([[ "$DO_NOT_NAG" != "true" ]] && (echo "$TOTAL_RUNS/10" | bc -l | grep -q "\.0*$")); then # Dialog 25: Donation nag ASK=$("$CD" msgbox --title "$STRING25a" --text "$STRING25b" --informative-text "$STRING25c $TOTAL_RUNS $STRING25d $(format_datasize $TOTAL_DATA_SIZE) $STRING25e" --button1 "$STRING25f" --button2 "$STRING25g" --button3 "$STRING25h" --no-newline) case "$ASK" in "1") ;; "2") open "http://s1.amazon.com/paypage/PCIAODJIUOEET";; "3") # Dialog 26: Thanks for donation "$CD" msgbox --title "$STRING26a" --text "$STRING26b" --button1 "$STRING26c" > /dev/null set_pref do_not_nag true;; esac fi } ### Formats datasize to be pretty format_datasize() { if (( "$1" < 1024 )); then RESULT="$1 B" elif (( "$1" < 1024 * 1024 )); then RESULT="$(( $1 / 1024 )) KB" elif (( "$1" < 1024 * 1024 * 1024 )); then RESULT="$(( $1 / 1024 / 1024 )) MB" else RESULT="$(( $1 / 1024 / 1024 / 1024 )) GB" fi echo "$RESULT" } ### Checks to see if a new version of iPodBackup is available update_check() { if ([[ "$CHECK_FOR_UPDATES" != "false" ]] && (ping -c 1 -t 3 www.google.com | grep -q "1 packets received")); then NEWEST_VERSION=($(curl -Ls "$HOMEPAGE/software/$(echo $APPNAME | tr "[:upper:]" "[:lower:]")/properties.php?print")) if ([[ ! -z "$NEWEST_VERSION" ]] && ([[ "$(get_newest ${NEWEST_VERSION[0]} $VERSION ".")" = "1" ]] || [[ "$(get_newest ${NEWEST_VERSION[1]} $DATE "-")" = "1" ]]) ); then echo "Update check: Newer version is available: ${NEWEST_VERSION[0]} (${NEWEST_VERSION[1]})" >> "$LOGFILE" # Dialog 28: New version available ASK=$("$CD" msgbox --title "$STRING28a" --text "$STRING28b" --informative-text "$STRING28c$VERSION ($DATE)$STRING28d${NEWEST_VERSION[0]} (${NEWEST_VERSION[1]})$STRING28e" --button1 "$STRING28f" --button2 "$STRING28g" --button3 "$STRING28h" --no-newline) case "$ASK" in "1") open $HOMEPAGE/software/?download=$APPNAME;; "2") ;; "3") set_pref check_for_updates false;; esac else echo "Update check: Current version is up-to-date" >> "$LOGFILE" fi fi } ### Compares two strings (either a date or a version) to see which is newer. ### Must specify the type of string as third argument. ### Returns 1 if $1 is newer, 2 for $2, or 0 if both are same get_newest() { if [[ "$#" != "3" ]]; then echo "Error attempting to compare \"$1\" and \"$2\": Bad arguments" >> "$LOGFILE" exit 1 fi FS=$3 for A in 1 2 3; do NUM1[$A]=$(echo "$1" | awk -F$FS "{print \$$A}" | sed -e "s/^0//") NUM2[$A]=$(echo "$2" | awk -F$FS "{print \$$A}" | sed -e "s/^0//") if (( "${NUM1[$A]}" > "${NUM2[$A]}" )); then echo "1" return elif (( "${NUM1[$A]}" < "${NUM2[$A]}" )); then echo "2" return fi done echo "0" } ### The real deal. This is where the code is actually executed. mkdir -m 700 "$HOME"/Library/Logs 2> /dev/null echo "$APPNAME version $VERSION ($DATE) running on $(uname -sr)" > "$LOGFILE" echo "Please contact $EMAIL if you have any problems" >> "$LOGFILE" date >> "$LOGFILE" get_language load_preferences test_permissions "$LOGFILE" handle_arguments "$@" test_for_ipod if [[ ! -d "$DESTINATION" && ! -f "$DISKIMAGE" ]]; then first_time_setup load_preferences fi preflight_prompt send_growl_notification "$STRING31a $IPODNAME $STRING31b" prepare_destination execute_backup error_handling $? unmount_diskimage postflight_chime send_growl_notification "$STRING32a" update_check echo "$APPNAME quit at "$(date) >> "$LOGFILE"