diff options
-rwxr-xr-x | bin/etc-update | 643 |
1 files changed, 338 insertions, 305 deletions
diff --git a/bin/etc-update b/bin/etc-update index 731b6484e..405822943 100755 --- a/bin/etc-update +++ b/bin/etc-update @@ -1,8 +1,9 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # Author Brandon Low <lostlogic@gentoo.org> +# Mike Frysinger <vapier@gentoo.org> # # Previous version (from which I've borrowed a few bits) by: # Jochem Kossen <j.kossen@home.nl> @@ -11,9 +12,7 @@ cd / -if type -P gsed >/dev/null ; then - sed() { gsed "$@"; } -fi +type -P gsed >/dev/null && sed() { gsed "$@"; } get_config() { # the sed here does: @@ -25,10 +24,10 @@ get_config() { # If there's more than one of the same configuration item, then # the store to the hold space clobbers previous value so the last # setting takes precedence. - local item=$1 - eval echo $(sed -n \ + local match=$1 + eval $(sed -n -r \ -e 's:[[:space:]]*#.*$::' \ - -e "/^[[:space:]]*$item[[:space:]]*=/{s:[^=]*=[[:space:]]*\([\"']\{0,1\}\)\(.*\)\1:\2:;h}" \ + -e "/^[[:space:]]*${match}[[:space:]]*=/{s:^([^=]*)=[[:space:]]*([\"']{0,1})(.*)\2:\1=\2\3\2:;H}" \ -e '${g;p}' \ "${PORTAGE_CONFIGROOT}"etc/etc-update.conf) } @@ -46,39 +45,49 @@ diff_command() { scan() { echo "Scanning Configuration files..." - rm -rf ${TMP}/files > /dev/null 2>&1 - mkdir ${TMP}/files || die "Failed mkdir command!" 1 + rm -rf "${TMP}"/files > /dev/null 2>&1 + mkdir "${TMP}"/files || die "Failed mkdir command!" count=0 input=0 local find_opts - local my_basename + local path for path in ${CONFIG_PROTECT} ; do - path="${EROOT}${path}" + path="${EROOT%/}${path}" + + [[ -w ${path} ]] || die "Need write access to ${path}" + # Do not traverse hidden directories such as .svn or .git. - find_opts="-name .* -type d -prune -o -name ._cfg????_*" - if [ ! -d "${path}" ]; then - [ ! -f "${path}" ] && continue - my_basename="${path##*/}" + find_opts=( -name '.*' -type d -prune -o -name '._cfg????_*' ) + if [[ ! -d ${path} ]] ; then + [[ ! -f ${path} ]] && continue + local my_basename="${path##*/}" path="${path%/*}" - find_opts="-maxdepth 1 -name ._cfg????_${my_basename}" + find_opts+=( -maxdepth 1 -name "._cfg????_${my_basename}" ) fi - - ofile="" - # The below set -f turns off file name globbing in the ${find_opts} expansion. - for file in $(set -f ; find ${path}/ ${find_opts} \ - ! -name '.*~' ! -iname '.*.bak' -print | - sed -e "s:\(^.*/\)\(\._cfg[0-9]*_\)\(.*$\):\1\2\3\%\1%\2\%\3:" | - sort -t'%' -k2,2 -k4,4 -k3,3 | LANG=POSIX LC_ALL=POSIX cut -f1 -d'%'); do - - rpath=$(echo "${file/\/\///}" | sed -e "s:/[^/]*$::") - rfile=$(echo "${file/\/\///}" | sed -e "s:^.*/::") + find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print ) + + local file ofile b=$'\001' + for file in $(find "${path}"/ "${find_opts[@]}" | + sed \ + -e 's://*:/:g' \ + -e "s:\(^.*/\)\(\._cfg[0-9]*_\)\(.*$\):\1\2\3$b\1$b\2$b\3:" | + sort -t"$b" -k2,2 -k4,4 -k3,3 | + LC_ALL=C cut -f1 -d"$b") + do + local rpath rfile cfg_file live_file + rpath=${file%/*} + rfile=${file##*/} + cfg_file="${rpath}/${rfile}" + live_file="${rpath}/${rfile:10}" + + local mpath for mpath in ${CONFIG_PROTECT_MASK}; do - mpath="${EROOT}${mpath}" - mpath=$(echo "${mpath/\/\///}") - if [[ "${rpath}" == "${mpath}"* ]]; then - mv ${rpath}/${rfile} ${rpath}/${rfile:10} - break + mpath="${EROOT%/}${mpath}" + if [[ "${rpath}" == "${mpath}"* ]] ; then + echo "Updating masked file: ${live_file}" + mv "${cfg_file}" "${live_file}" + continue 2 fi done if [[ ! -f ${file} ]] ; then @@ -87,39 +96,53 @@ scan() { fi if [[ "${ofile:10}" != "${rfile:10}" ]] || - [[ ${opath} != ${rpath} ]]; then + [[ ${opath} != ${rpath} ]] + then MATCHES=0 - if [[ "${EU_AUTOMERGE}" == "yes" ]]; then - if [ ! -e "${rpath}/${rfile}" ] || [ ! -e "${rpath}/${rfile:10}" ]; then + if [[ ${eu_automerge} == "yes" ]] ; then + if [[ ! -e ${cfg_file} || ! -e ${live_file} ]] ; then MATCHES=0 else - diff -Bbua ${rpath}/${rfile} ${rpath}/${rfile:10} | egrep '^[+-]' | egrep -v '^[+-][\t ]*#|^--- |^\+\+\+ ' | egrep -qv '^[-+][\t ]*$' + diff -Bbua "${cfg_file}" "${live_file}" | \ + sed -n -r \ + -e '/^[+-]/{/^([+-][\t ]*(#|$)|-{3} |\+{3} )/d;q0}' \ + -e '$q1' MATCHES=$? fi - elif [[ -z $(diff -Nua ${rpath}/${rfile} ${rpath}/${rfile:10}| - grep "^[+-][^+-]"|grep -v '# .Header:.*') ]]; then + + elif diff -Nua "${cfg_file}" "${live_file}" | + sed -n \ + -e '/# .Header:/d' \ + -e '/^[+-][^+-]/q1' \ + -e '$q0' + then MATCHES=1 fi - if [[ "${MATCHES}" == "1" ]]; then - echo "Automerging trivial changes in: ${rpath}/${rfile:10}" - mv ${rpath}/${rfile} ${rpath}/${rfile:10} + + if [[ ${MATCHES} == 1 ]] ; then + echo "Automerging trivial changes in: ${live_file}" + mv "${cfg_file}" "${live_file}" continue else - count=${count}+1 - echo "${rpath}/${rfile:10}" > ${TMP}/files/${count} - echo "${rpath}/${rfile}" >> ${TMP}/files/${count} + : $(( ++count )) + echo "${live_file}" > "${TMP}"/files/${count} + echo "${cfg_file}" >> "${TMP}"/files/${count} ofile="${rfile}" opath="${rpath}" continue fi fi - if [[ -z $(diff -Nua ${rpath}/${rfile} ${rpath}/${ofile}| - grep "^[+-][^+-]"|grep -v '# .Header:.*') ]]; then - mv ${rpath}/${rfile} ${rpath}/${ofile} + if diff -Nua "${cfg_file}" "${rpath}/${ofile}" | + sed -n \ + -e '/# .Header:/d' \ + -e '/^[+-][^+-]/q1' \ + -e '$q0' + then + mv "${cfg_file}" "${rpath}/${ofile}" continue else - echo "${rpath}/${rfile}" >> ${TMP}/files/${count} + echo "${cfg_file}" >> "${TMP}"/files/${count} ofile="${rfile}" opath="${rpath}" fi @@ -134,60 +157,63 @@ sel_file() { [[ ${input} == -1 ]] || \ [[ ${input} == -3 ]] do - local numfiles=$(ls ${TMP}/files|wc -l) - local numwidth=${#numfiles} - for file in $(ls ${TMP}/files|sort -n); do - if [[ ${isfirst} == 0 ]] ; then - isfirst=${file} - fi - numshow=$(printf "%${numwidth}i${PAR} " ${file}) - numupdates=$(( $(wc -l <${TMP}/files/${file}) - 1 )) - echo -n "${numshow}" - if [[ ${mode} == 0 ]] ; then - echo "$(head -n1 ${TMP}/files/${file}) (${numupdates})" - else - head -n1 ${TMP}/files/${file} - fi - done > ${TMP}/menuitems + local allfiles=( $(cd "${TMP}"/files/ && printf '%s\n' * | sort -n) ) + local isfirst=${allfiles[0]} - if [ "${OVERWRITE_ALL}" == "yes" ]; then - input=0 - elif [ "${DELETE_ALL}" == "yes" ]; then + # Optimize: no point in building the whole file list if + # we're not actually going to talk to the user. + if [[ ${OVERWRITE_ALL} == "yes" || ${DELETE_ALL} == "yes" ]] ; then input=0 else - [[ $CLEAR_TERM == yes ]] && clear - if [[ ${mode} == 0 ]] ; then - echo "The following is the list of files which need updating, each -configuration file is followed by a list of possible replacement files." - else - local my_title="Please select a file to update" - fi + local numfiles=${#allfiles[@]} + local numwidth=${#numfiles} + local file fullfile line + for file in "${allfiles[@]}" ; do + fullfile="${TMP}/files/${file}" + line=$(head -n1 "${fullfile}") + printf '%*i%s %s' ${numwidth} ${file} "${PAR}" "${line}" + if [[ ${mode} == 0 ]] ; then + local numupdates=$(( $(wc -l <"${fullfile}") - 1 )) + echo " (${numupdates})" + else + echo + fi + done > "${TMP}"/menuitems + + clear if [[ ${mode} == 0 ]] ; then - cat ${TMP}/menuitems - echo "Please select a file to edit by entering the corresponding number." - echo " (don't use -3, -5, -7 or -9 if you're unsure what to do)" - echo " (-1 to exit) (-3 to auto merge all remaining files)" - echo " (-5 to auto-merge AND not use 'mv -i')" - echo " (-7 to discard all updates)" - echo -n " (-9 to discard all updates AND not use 'rm -i'): " + cat <<-EOF + The following is the list of files which need updating, each + configuration file is followed by a list of possible replacement files. + $(<"${TMP}"/menuitems) + Please select a file to edit by entering the corresponding number. + (don't use -3, -5, -7 or -9 if you're unsure what to do) + (-1 to exit) (-3 to auto merge all remaining files) + (-5 to auto-merge AND not use 'mv -i') + (-7 to discard all updates) + EOF + printf " (-9 to discard all updates AND not use 'rm -i'): " input=$(read_int) else - dialog --title "${title}" --menu "${my_title}" \ - 0 0 0 $(echo -e "-1 Exit\n$(<${TMP}/menuitems)") \ - 2> ${TMP}/input || die "User termination!" 0 - input=$(<${TMP}/input) + dialog \ + --title "${title}" \ + --menu "Please select a file to update" \ + 0 0 0 $(<"${TMP}"/menuitems) \ + 2> "${TMP}"/input \ + || die "$(<"${TMP}"/input)\n\nUser termination!" 0 + input=$(<"${TMP}"/input) fi - if [[ ${input} == -9 ]]; then - read -p "Are you sure that you want to delete all updates (type YES):" reply - if [[ ${reply} != "YES" ]]; then + if [[ ${input} == -9 ]] ; then + read -p "Are you sure that you want to delete all updates (type YES): " reply + if [[ ${reply} != "YES" ]] ; then continue else input=-7 export rm_opts="" fi fi - if [[ ${input} == -7 ]]; then + if [[ ${input} == -7 ]] ; then input=0 export DELETE_ALL="yes" fi @@ -201,21 +227,24 @@ configuration file is followed by a list of possible replacement files." export OVERWRITE_ALL="yes" fi fi # -3 automerge - if [[ -z ${input} ]] || [[ ${input} == 0 ]] ; then + if [[ ${input:-0} == 0 ]] ; then input=${isfirst} fi done } user_special() { - if [ -r ${PORTAGE_CONFIGROOT}etc/etc-update.special ]; then - if [ -z "$1" ]; then - echo "ERROR: user_special() called without arguments" + local special="${PORTAGE_CONFIGROOT}etc/etc-update.special" + + if [[ -r ${special} ]] ; then + if [[ -z $1 ]] ; then + error "user_special() called without arguments" return 1 fi - while read -r pat; do - echo ${1} | grep "${pat}" > /dev/null && return 0 - done < ${PORTAGE_CONFIGROOT}etc/etc-update.special + local pat + while read -r pat ; do + echo "$1" | grep -q "${pat}" && return 0 + done < "${special}" fi return 1 } @@ -225,12 +254,12 @@ read_int() { # read. This is a workaround for odd behavior of bash when an attempt is # made to store a value such as "1y" into an integer-only variable. local my_input - while true; do + while : ; do read my_input # failed integer conversions will break a loop unless they're enclosed # in a subshell. - echo "${my_input}" | ( declare -i x; read x) 2>/dev/null && break - echo -n "Value '$my_input' is not valid. Please enter an integer value:" >&2 + echo "${my_input}" | (declare -i x; read x) 2>/dev/null && break + printf 'Value "%s" is not valid. Please enter an integer value: ' "${my_input}" >&2 done echo ${my_input} } @@ -239,141 +268,147 @@ do_file() { interactive_echo() { [ "${OVERWRITE_ALL}" != "yes" ] && [ "${DELETE_ALL}" != "yes" ] && echo; } interactive_echo local -i my_input - local -i fcount=0 - until (( $(wc -l < ${TMP}/files/${input}) < 2 )); do - my_input=0 - if (( $(wc -l < ${TMP}/files/${input}) == 2 )); then + local -i linecnt + local fullfile="${TMP}/files/${input}" + local ofile=$(head -n1 "${fullfile}") + + # Walk through all the pending updates for this one file. + linecnt=$(wc -l <"${fullfile}") + while (( linecnt > 1 )) ; do + if (( linecnt == 2 )) ; then + # Only one update ... keeps things simple. my_input=1 + else + my_input=0 fi - until (( ${my_input} > 0 )) && (( ${my_input} < $(wc -l < ${TMP}/files/${input}) )); do - fcount=0 - if [ "${OVERWRITE_ALL}" == "yes" ]; then - my_input=0 - elif [ "${DELETE_ALL}" == "yes" ]; then - my_input=0 - else - for line in $(<${TMP}/files/${input}); do - if (( ${fcount} > 0 )); then - echo -n "${fcount}${PAR} " - echo "${line}" - else - if [[ ${mode} == 0 ]] ; then - echo "Below are the new config files for ${line}:" - else - local my_title="Please select a file to process for ${line}" - fi - fi - fcount=${fcount}+1 - done > ${TMP}/menuitems + # Optimize: no point in scanning the file list when we know + # we're just going to consume all the ones available. + if [[ ${OVERWRITE_ALL} == "yes" || ${DELETE_ALL} == "yes" ]] ; then + my_input=1 + fi - if [[ ${mode} == 0 ]] ; then - cat ${TMP}/menuitems - echo -n "Please select a file to process (-1 to exit this file): " - my_input=$(read_int) - else - dialog --title "${title}" --menu "${my_title}" \ - 0 0 0 $(echo -e "$(<${TMP}/menuitems)\n${fcount} Exit") \ - 2> ${TMP}/input || die "User termination!" 0 - my_input=$(<${TMP}/input) + # Figure out which file they wish to operate on. + while (( my_input <= 0 || my_input >= linecnt )) ; do + local fcount=0 + for line in $(<"${fullfile}"); do + if (( fcount > 0 )); then + printf '%i%s %s\n' ${fcount} "${PAR}" "${line}" fi - fi # OVERWRITE_ALL + : $(( ++fcount )) + done > "${TMP}"/menuitems + + if [[ ${mode} == 0 ]] ; then + echo "Below are the new config files for ${ofile}:" + cat "${TMP}"/menuitems + echo -n "Please select a file to process (-1 to exit this file): " + my_input=$(read_int) + else + dialog \ + --title "${title}" \ + --menu "Please select a file to process for ${ofile}" \ + 0 0 0 $(<"${TMP}"/menuitems) \ + 2> "${TMP}"/input \ + || die "$(<"${TMP}"/input)\n\nUser termination!" 0 + my_input=$(<"${TMP}"/input) + fi if [[ ${my_input} == 0 ]] ; then + # Auto select the first file. my_input=1 elif [[ ${my_input} == -1 ]] ; then input=0 return - elif [[ ${my_input} == ${fcount} ]] ; then - break fi done - if [[ ${my_input} == ${fcount} ]] ; then - break - fi - - fcount=${my_input}+1 - - file=$(sed -e "${fcount}p;d" ${TMP}/files/${input}) - ofile=$(head -n1 ${TMP}/files/${input}) + # First line is the old file while the rest are the config files. + : $(( ++my_input )) + local file=$(sed -n -e "${my_input}p" "${fullfile}") do_cfg "${file}" "${ofile}" - sed -e "${fcount}!p;d" ${TMP}/files/${input} > ${TMP}/files/sed - mv ${TMP}/files/sed ${TMP}/files/${input} + sed -i -e "${my_input}d" "${fullfile}" - if [[ ${my_input} == -1 ]] ; then - break - fi + : $(( --linecnt )) done + interactive_echo - rm ${TMP}/files/${input} - count=${count}-1 + rm "${fullfile}" + : $(( --count )) } -do_cfg() { +show_diff() { + clear + local file1=$1 file2=$2 + if [[ ${using_editor} == 0 ]] ; then + ( + echo "Showing differences between ${file1} and ${file2}" + diff_command "${file1}" "${file2}" + ) | ${pager} + else + echo "Beginning of differences between ${file1} and ${file2}" + diff_command "${file1}" "${file2}" + echo "End of differences between ${file1} and ${file2}" + fi +} - local file="${1}" - local ofile="${2}" +do_cfg() { + local file=$1 + local ofile=$2 local -i my_input=0 - until (( ${my_input} == -1 )) || [ ! -f ${file} ]; do + until (( my_input == -1 )) || [ ! -f ${file} ] ; do if [[ "${OVERWRITE_ALL}" == "yes" ]] && ! user_special "${ofile}"; then my_input=1 elif [[ "${DELETE_ALL}" == "yes" ]] && ! user_special "${ofile}"; then my_input=2 else - [[ $CLEAR_TERM == yes ]] && clear - if [ "${using_editor}" == 0 ]; then - ( - echo "Showing differences between ${ofile} and ${file}" - diff_command "${ofile}" "${file}" - ) | ${pager} - else - echo "Beginning of differences between ${ofile} and ${file}" - diff_command "${ofile}" "${file}" - echo "End of differences between ${ofile} and ${file}" - fi - if [ -L "${file}" ]; then - echo - echo "-------------------------------------------------------------" - echo "NOTE: File is a symlink to another file. REPLACE recommended." - echo " The original file may simply have moved. Please review." - echo "-------------------------------------------------------------" - echo + show_diff "${ofile}" "${file}" + if [[ -L ${file} ]] ; then + cat <<-EOF + + ------------------------------------------------------------- + NOTE: File is a symlink to another file. REPLACE recommended. + The original file may simply have moved. Please review. + ------------------------------------------------------------- + + EOF fi - echo -n "File: ${file} -1) Replace original with update -2) Delete update, keeping original as is -3) Interactively merge original with update -4) Show differences again -5) Save update as example config -Please select from the menu above (-1 to ignore this update): " + cat <<-EOF + + File: ${file} + 1) Replace original with update + 2) Delete update, keeping original as is + 3) Interactively merge original with update + 4) Show differences again + 5) Save update as example config + EOF + printf 'Please select from the menu above (-1 to ignore this update): ' my_input=$(read_int) fi case ${my_input} in - 1) echo "Replacing ${ofile} with ${file}" - mv ${mv_opts} ${file} ${ofile} - [ -n "${OVERWRITE_ALL}" ] && my_input=-1 - continue - ;; - 2) echo "Deleting ${file}" - rm ${rm_opts} ${file} - [ -n "${DELETE_ALL}" ] && my_input=-1 - continue - ;; - 3) do_merge "${file}" "${ofile}" - my_input=${?} -# [ ${my_input} == 255 ] && my_input=-1 - continue - ;; - 4) continue - ;; - 5) do_distconf "${file}" "${ofile}" - ;; - *) continue - ;; + 1) echo "Replacing ${ofile} with ${file}" + mv ${mv_opts} ${file} ${ofile} + [ -n "${OVERWRITE_ALL}" ] && my_input=-1 + continue + ;; + 2) echo "Deleting ${file}" + rm ${rm_opts} ${file} + [ -n "${DELETE_ALL}" ] && my_input=-1 + continue + ;; + 3) do_merge "${file}" "${ofile}" + my_input=${?} +# [ ${my_input} == 255 ] && my_input=-1 + continue + ;; + 4) continue + ;; + 5) do_distconf "${file}" "${ofile}" + ;; + *) continue + ;; esac done } @@ -399,57 +434,48 @@ do_merge() { # need to make sure the full /path/to/ exists ahead of time mkdir -p "${mfile%/*}" - until (( ${my_input} == -1 )); do + until (( my_input == -1 )); do echo "Merging ${file} and ${ofile}" $(echo "${merge_command}" | sed -e "s:%merged:${mfile}:g" \ -e "s:%orig:${ofile}:g" \ -e "s:%new:${file}:g") - until (( ${my_input} == -1 )); do - echo -n "1) Replace ${ofile} with merged file -2) Show differences between merged file and original -3) Remerge original with update -4) Edit merged file -5) Return to the previous menu -Please select from the menu above (-1 to exit, losing this merge): " + until (( my_input == -1 )); do + cat <<-EOF + 1) Replace ${ofile} with merged file + 2) Show differences between merged file and original + 3) Remerge original with update + 4) Edit merged file + 5) Return to the previous menu + EOF + printf 'Please select from the menu above (-1 to exit, losing this merge): ' my_input=$(read_int) case ${my_input} in - 1) echo "Replacing ${ofile} with ${mfile}" - if [[ ${USERLAND} == BSD ]] ; then - chown "$(stat -f %Su:%Sg "${ofile}")" "${mfile}" - chmod $(stat -f %Mp%Lp "${ofile}") "${mfile}" - else - chown --reference="${ofile}" "${mfile}" - chmod --reference="${ofile}" "${mfile}" - fi - mv ${mv_opts} "${mfile}" "${ofile}" - rm ${rm_opts} "${file}" - return 255 - ;; - 2) - [[ $CLEAR_TERM == yes ]] && clear - if [ "${using_editor}" == 0 ]; then - ( - echo "Showing differences between ${ofile} and ${mfile}" - diff_command "${ofile}" "${mfile}" - ) | ${pager} + 1) echo "Replacing ${ofile} with ${mfile}" + if [[ ${USERLAND} == BSD ]] ; then + chown "$(stat -f %Su:%Sg "${ofile}")" "${mfile}" + chmod $(stat -f %Mp%Lp "${ofile}") "${mfile}" else - echo "Beginning of differences between ${ofile} and ${mfile}" - diff_command "${ofile}" "${mfile}" - echo "End of differences between ${ofile} and ${mfile}" + chown --reference="${ofile}" "${mfile}" + chmod --reference="${ofile}" "${mfile}" fi - continue - ;; - 3) break - ;; - 4) ${EDITOR:-nano -w} "${mfile}" - continue - ;; - 5) rm ${rm_opts} "${mfile}" - return 0 - ;; - *) continue - ;; + mv ${mv_opts} "${mfile}" "${ofile}" + rm ${rm_opts} "${file}" + return 255 + ;; + 2) show_diff "${ofile}" "${mfile}" + continue + ;; + 3) break + ;; + 4) ${EDITOR:-nano -w} "${mfile}" + continue + ;; + 5) rm ${rm_opts} "${mfile}" + return 0 + ;; + *) continue + ;; esac done done @@ -461,21 +487,15 @@ do_distconf() { # search for any previously saved distribution config # files and number the current one accordingly - local file="${1}" - local ofile="${2}" + local file=$1 ofile=$2 local -i count - local -i fill local suffix local efile - for ((count = 0; count <= 9999; count++)); do - suffix=".dist_" - for ((fill = 4 - ${#count}; fill > 0; fill--)); do - suffix+="0" - done - suffix+="${count}" + for (( count = 0; count <= 9999; ++count )) ; do + suffix=$(printf ".dist_%04i" ${count}) efile="${ofile}${suffix}" - if [[ ! -f ${efile} ]]; then + if [[ ! -f ${efile} ]] ; then mv ${mv_opts} "${file}" "${efile}" break elif diff_command "${file}" "${efile}" &> /dev/null; then @@ -486,20 +506,22 @@ do_distconf() { done } +error() { echo "etc-update: ERROR: $*" 1>&2 ; return 1 ; } die() { trap SIGTERM trap SIGINT + local msg=$1 exitcode=${2:-1} - if [ "$2" -eq 0 ]; then - echo "Exiting: ${1}" + if [ ${exitcode} -eq 0 ] ; then + printf 'Exiting: %b\n' "${msg}" scan > /dev/null [ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining" else - echo "ERROR: ${1}" + error "${msg}" fi rm -rf "${TMP}" - exit ${2} + exit ${exitcode} } usage() { @@ -511,10 +533,11 @@ usage() { Options: -d, --debug Enable shell debugging -h, --help Show help and run away + -v, --verbose Show settings and such along the way -V, --version Show version and trundle away EOF - [[ -n ${*:2} ]] && printf "\nError: %s\n" "${*:2}" 1>&2 + [[ $# -gt 1 ]] && printf "\nError: %s\n" "${*:2}" 1>&2 exit ${1:-0} } @@ -524,10 +547,12 @@ usage() { # SET_X=false +VERBOSE=false while [[ -n $1 ]] ; do case $1 in -d|--debug) SET_X=true;; -h|--help) usage;; + -v|--verbose) VERBOSE=true;; -V|--version) emerge --version ; exit 0;; *) usage 1 "Invalid option '$1'";; esac @@ -535,77 +560,85 @@ while [[ -n $1 ]] ; do done ${SET_X} && set -x -type portageq > /dev/null || exit $? -eval $(portageq envvar -v CONFIG_PROTECT \ - CONFIG_PROTECT_MASK PORTAGE_CONFIGROOT PORTAGE_INST_GID PORTAGE_INST_UID \ - PORTAGE_TMPDIR EROOT USERLAND) +type portageq >/dev/null || die "missing portageq" +portage_vars=( + CONFIG_PROTECT{,_MASK} + PORTAGE_CONFIGROOT + PORTAGE_INST_{G,U}ID + PORTAGE_TMPDIR + EROOT + USERLAND + NOCOLOR +) +eval $(portageq envvar -v ${portage_vars[@]}) export PORTAGE_TMPDIR TMP="${PORTAGE_TMPDIR}/etc-update-$$" -trap "die terminated 1" SIGTERM -trap "die interrupted 1" SIGINT - -[ -w ${PORTAGE_CONFIGROOT}etc ] || die "Need write access to ${PORTAGE_CONFIGROOT}etc" 1 -#echo $PORTAGE_TMPDIR -#echo $CONFIG_PROTECT -#echo $CONFIG_PROTECT_MASK -#export PORTAGE_TMPDIR=$(/usr/lib/portage/bin/portageq envvar PORTAGE_TMPDIR) +trap "die terminated" SIGTERM +trap "die interrupted" SIGINT -rm -rf "${TMP}" 2> /dev/null -mkdir "${TMP}" || die "failed to create temp dir" 1 +rm -rf "${TMP}" 2>/dev/null +mkdir "${TMP}" || die "failed to create temp dir" # make sure we have a secure directory to work in -chmod 0700 "${TMP}" || die "failed to set perms on temp dir" 1 +chmod 0700 "${TMP}" || die "failed to set perms on temp dir" chown ${PORTAGE_INST_UID:-0}:${PORTAGE_INST_GID:-0} "${TMP}" || \ - die "failed to set ownership on temp dir" 1 - -# I need the CONFIG_PROTECT value -#CONFIG_PROTECT=$(/usr/lib/portage/bin/portageq envvar CONFIG_PROTECT) -#CONFIG_PROTECT_MASK=$(/usr/lib/portage/bin/portageq envvar CONFIG_PROTECT_MASK) - -# load etc-config's configuration -CLEAR_TERM=$(get_config clear_term) -EU_AUTOMERGE=$(get_config eu_automerge) -rm_opts=$(get_config rm_opts) -mv_opts=$(get_config mv_opts) -cp_opts=$(get_config cp_opts) -pager=$(get_config pager) -diff_command=$(get_config diff_command) -using_editor=$(get_config using_editor) -merge_command=$(get_config merge_command) -declare -i mode=$(get_config mode) -[[ -z ${mode} ]] && mode=0 + die "failed to set ownership on temp dir" + +# Get all the user settings from etc-update.conf +cfg_vars=( + using_editor + clear_term + eu_automerge + rm_opts + mv_opts + pager + diff_command + using_editor + merge_command + mode +) +# default them all to "" +eval ${cfg_vars[@]/%/=} +# then extract them all from the conf in one shot +# (ugly var at end is due to printf appending a '|' to last item) +get_config "($(printf '%s|' "${cfg_vars[@]}")NOVARFOROLDMEN)" + +# finally setup any specific defaults +: ${mode:="0"} if ! cmd_var_is_valid "${pager}" ; then pager=${PAGER} cmd_var_is_valid "${pager}" || pager=cat fi -if [ "${using_editor}" == 0 ]; then +[[ ${clear_term} == "yes" ]] || clear() { :; } + +if [[ ${using_editor} == "0" ]] ; then # Sanity check to make sure diff exists and works echo > "${TMP}"/.diff-test-1 echo > "${TMP}"/.diff-test-2 - + if ! diff_command "${TMP}"/.diff-test-1 "${TMP}"/.diff-test-2 ; then - die "'${diff_command}' does not seem to work, aborting" 1 + die "'${diff_command}' does not seem to work, aborting" fi else - if ! type ${diff_command%% *} >/dev/null; then - die "'${diff_command}' does not seem to work, aborting" 1 + if ! cmd_var_is_valid "${diff_command}" ; then + die "'${diff_command}' does not seem to work, aborting" fi fi -if [[ ${mode} == "1" ]] ; then +if [[ ${mode} == "0" ]] ; then + PAR=")" +else + PAR="" if ! type dialog >/dev/null || ! dialog --help >/dev/null ; then - die "mode=1 and 'dialog' not found or not executable, aborting" 1 + die "mode=1 and 'dialog' not found or not executable, aborting" fi fi -#echo "rm_opts: $rm_opts, mv_opts: $mv_opts, cp_opts: $cp_opts" -#echo "pager: $pager, diff_command: $diff_command, merge_command: $merge_command" - -if (( ${mode} == 0 )); then - PAR=")" -else - PAR="" +if ${VERBOSE} ; then + for v in ${portage_vars[@]} ${cfg_vars[@]} TMP ; do + echo "${v}=${!v}" + done fi declare -i count=0 @@ -614,12 +647,12 @@ declare title="Gentoo's etc-update tool!" scan -until (( ${input} == -1 )); do - if (( ${count} == 0 )); then +until (( input == -1 )); do + if (( count == 0 )); then die "Nothing left to do; exiting. :)" 0 fi sel_file - if (( ${input} != -1 )); then + if (( input != -1 )); then do_file fi done |