| #!/bin/bash |
| # 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> |
| # Leo Lipelis <aeoo@gentoo.org> |
| # Karl Trygve Kalleberg <karltk@gentoo.org> |
| |
| cd / |
| |
| type -P gsed >/dev/null && sed() { gsed "$@"; } |
| |
| get_config() { |
| # the sed here does: |
| # - strip off comments |
| # - match lines that set item in question |
| # - delete the "item =" part |
| # - store the actual value into the hold space |
| # - on the last line, restore the hold space and print it |
| # 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 match=$1 |
| eval $(sed -n -r \ |
| -e 's:[[:space:]]*#.*$::' \ |
| -e "/^[[:space:]]*${match}[[:space:]]*=/{s:^([^=]*)=[[:space:]]*([\"']{0,1})(.*)\2:\1=\2\3\2:;H}" \ |
| -e '${g;p}' \ |
| "${PORTAGE_CONFIGROOT}"etc/etc-update.conf) |
| } |
| |
| cmd_var_is_valid() { |
| # return true if the first whitespace-separated token contained |
| # in "${1}" is an executable file, false otherwise |
| [[ -x $(type -P ${1%%[[:space:]]*}) ]] |
| } |
| |
| diff_command() { |
| local cmd=${diff_command//%file1/$1} |
| ${cmd//%file2/$2} |
| } |
| |
| # Usage: do_mv_ln [options] <src> <dst> |
| # Files have to be the last two args, and has to be |
| # files so we can handle symlinked target sanely. |
| do_mv_ln() { |
| local opts=( ${@:1:$(( $# - 2 ))} ) |
| local src=${@:$(( $# - 1 )):1} |
| local dst=${@:$(( $# - 0 )):1} |
| |
| if [[ -L ${dst} ]] ; then #330221 |
| local lfile=$(readlink "${dst}") |
| [[ ${lfile} == /* ]] || lfile="${dst%/*}/${lfile}" |
| echo " Target is a symlink; replacing ${lfile}" |
| dst=${lfile} |
| fi |
| |
| mv "${opts[@]}" "${src}" "${dst}" |
| } |
| |
| scan() { |
| ${QUIET} || echo "Scanning Configuration files..." |
| rm -rf "${TMP}"/files > /dev/null 2>&1 |
| mkdir "${TMP}"/files || die "Failed mkdir command!" |
| count=0 |
| input=0 |
| local find_opts |
| local path |
| |
| for path in ${SCAN_PATHS} ; do |
| path="${EROOT%/}${path}" |
| |
| if [[ ! -d ${path} ]] ; then |
| [[ ! -f ${path} ]] && continue |
| local my_basename="${path##*/}" |
| path="${path%/*}" |
| find_opts=( -maxdepth 1 -name "._cfg????_${my_basename}" ) |
| else |
| # Do not traverse hidden directories such as .svn or .git. |
| find_opts=( -name '.*' -type d -prune -o -name '._cfg????_*' ) |
| fi |
| find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print ) |
| |
| if [ ! -w "${path}" ] ; then |
| [ -e "${path}" ] || continue |
| die "Need write access to ${path}" |
| fi |
| |
| 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}" |
| if [[ "${rpath}" == "${mpath}"* ]] ; then |
| ${QUIET} || echo "Updating masked file: ${live_file}" |
| mv "${cfg_file}" "${live_file}" |
| continue 2 |
| fi |
| done |
| if [[ ! -f ${file} ]] ; then |
| ${QUIET} || echo "Skipping non-file ${file} ..." |
| continue |
| fi |
| |
| if [[ "${ofile:10}" != "${rfile:10}" ]] || |
| [[ ${opath} != ${rpath} ]] |
| then |
| MATCHES=0 |
| if [[ ${eu_automerge} == "yes" ]] ; then |
| if [[ ! -e ${cfg_file} || ! -e ${live_file} ]] ; then |
| MATCHES=0 |
| else |
| diff -Bbua "${cfg_file}" "${live_file}" | \ |
| sed -n -r \ |
| -e '/^[+-]/{/^([+-][\t ]*(#|$)|-{3} |\+{3} )/d;q1}' |
| : $(( MATCHES = ($? == 0) )) |
| fi |
| |
| else |
| diff -Nbua "${cfg_file}" "${live_file}" | |
| sed -n \ |
| -e '/# .Header:/d' \ |
| -e '/^[+-][^+-]/q1' |
| : $(( MATCHES = ($? == 0) )) |
| fi |
| |
| if [[ ${MATCHES} == 1 ]] ; then |
| ${QUIET} || echo "Automerging trivial changes in: ${live_file}" |
| do_mv_ln "${cfg_file}" "${live_file}" |
| continue |
| else |
| : $(( ++count )) |
| echo "${live_file}" > "${TMP}"/files/${count} |
| echo "${cfg_file}" >> "${TMP}"/files/${count} |
| ofile="${rfile}" |
| opath="${rpath}" |
| continue |
| fi |
| fi |
| |
| if ! diff -Nbua "${cfg_file}" "${rpath}/${ofile}" | |
| sed -n \ |
| -e '/# .Header:/d' \ |
| -e '/^[+-][^+-]/q1' |
| then |
| echo "${cfg_file}" >> "${TMP}"/files/${count} |
| ofile="${rfile}" |
| opath="${rpath}" |
| else |
| mv "${cfg_file}" "${rpath}/${ofile}" |
| continue |
| fi |
| done |
| done |
| } |
| |
| parse_automode_flag() { |
| case $1 in |
| -9) |
| local reply |
| read -p "Are you sure that you want to delete all updates (type YES): " reply |
| if [[ ${reply} != "YES" ]] ; then |
| echo "Did not get a 'YES', so ignoring request" |
| return 1 |
| else |
| parse_automode_flag -7 |
| export rm_opts="" |
| fi |
| ;; |
| -7) |
| input=0 |
| export DELETE_ALL="yes" |
| ;; |
| -5) |
| parse_automode_flag -3 |
| export mv_opts=" ${mv_opts} " |
| mv_opts="${mv_opts// -i / }" |
| NONINTERACTIVE_MV=true |
| ;; |
| -3) |
| input=0 |
| export OVERWRITE_ALL="yes" |
| ;; |
| *) |
| return 1 |
| ;; |
| esac |
| return 0 |
| } |
| |
| sel_file() { |
| local -i isfirst=0 |
| until [[ -f ${TMP}/files/${input} ]] || \ |
| [[ ${input} == -1 ]] || \ |
| [[ ${input} == -3 ]] |
| do |
| local allfiles=( $(cd "${TMP}"/files/ && printf '%s\n' * | sort -n) ) |
| local isfirst=${allfiles[0]} |
| |
| # 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 |
| 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 <<-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_HELP_TEXT}) |
| (${_5_HELP_TEXT}) |
| (${_7_HELP_TEXT}) |
| EOF |
| printf " (${_9_HELP_TEXT}): " |
| input=$(read_int) |
| else |
| 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 |
| : ${input:=0} |
| |
| if [[ ${input} != 0 ]] ; then |
| parse_automode_flag ${input} || continue |
| fi |
| fi # -3 automerge |
| if [[ ${input} == 0 ]] ; then |
| input=${isfirst} |
| fi |
| done |
| } |
| |
| user_special() { |
| 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 |
| local pat |
| while read -r pat ; do |
| echo "$1" | grep -q "${pat}" && return 0 |
| done < "${special}" |
| fi |
| return 1 |
| } |
| |
| read_int() { |
| # Read an integer from stdin. Continously loops until a valid integer is |
| # 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 : ; 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 |
| printf 'Value "%s" is not valid. Please enter an integer value: ' "${my_input}" >&2 |
| done |
| echo ${my_input} |
| } |
| |
| do_file() { |
| interactive_echo() { [ "${OVERWRITE_ALL}" != "yes" ] && [ "${DELETE_ALL}" != "yes" ] && echo; } |
| interactive_echo |
| local -i my_input |
| 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 |
| |
| # 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 |
| |
| # 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 |
| : $(( ++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 |
| fi |
| done |
| |
| # 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 -i -e "${my_input}d" "${fullfile}" |
| |
| : $(( --linecnt )) |
| done |
| |
| interactive_echo |
| rm "${fullfile}" |
| : $(( --count )) |
| } |
| |
| 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 |
| } |
| |
| do_cfg() { |
| local file=$1 |
| local ofile=$2 |
| local -i my_input=0 |
| |
| 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 |
| 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 |
| 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}" |
| do_mv_ln ${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 |
| } |
| |
| do_merge() { |
| # make sure we keep the merged file in the secure tempdir |
| # so we dont leak any information contained in said file |
| # (think of case where the file has 0600 perms; during the |
| # merging process, the temp file gets umask perms!) |
| |
| local file="${1}" |
| local ofile="${2}" |
| local mfile="${TMP}/${2}.merged" |
| local -i my_input=0 |
| echo "${file} ${ofile} ${mfile}" |
| |
| if [[ -e ${mfile} ]] ; then |
| echo "A previous version of the merged file exists, cleaning..." |
| rm ${rm_opts} "${mfile}" |
| fi |
| |
| # since mfile will be like $TMP/path/to/original-file.merged, we |
| # need to make sure the full /path/to/ exists ahead of time |
| mkdir -p "${mfile%/*}" |
| |
| 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 |
| 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 |
| do_mv_ln ${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 |
| rm ${rm_opts} "${mfile}" |
| return 255 |
| } |
| |
| do_distconf() { |
| # search for any previously saved distribution config |
| # files and number the current one accordingly |
| |
| local file=$1 ofile=$2 |
| local -i count |
| local suffix |
| local efile |
| |
| for (( count = 0; count <= 9999; ++count )) ; do |
| suffix=$(printf ".dist_%04i" ${count}) |
| efile="${ofile}${suffix}" |
| if [[ ! -f ${efile} ]] ; then |
| mv ${mv_opts} "${file}" "${efile}" |
| break |
| elif diff_command "${file}" "${efile}" &> /dev/null; then |
| # replace identical copy |
| mv "${file}" "${efile}" |
| break |
| fi |
| done |
| } |
| |
| error() { echo "etc-update: ERROR: $*" 1>&2 ; return 1 ; } |
| die() { |
| trap SIGTERM |
| trap SIGINT |
| local msg=$1 exitcode=${2:-1} |
| |
| if [ ${exitcode} -eq 0 ] ; then |
| ${QUIET} || printf 'Exiting: %b\n' "${msg}" |
| scan > /dev/null |
| ! ${QUIET} && [ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining" |
| else |
| error "${msg}" |
| fi |
| |
| rm -rf "${TMP}" |
| exit ${exitcode} |
| } |
| |
| _3_HELP_TEXT="-3 to auto merge all files" |
| _5_HELP_TEXT="-5 to auto-merge AND not use 'mv -i'" |
| _7_HELP_TEXT="-7 to discard all updates" |
| _9_HELP_TEXT="-9 to discard all updates AND not use 'rm -i'" |
| usage() { |
| cat <<-EOF |
| etc-update: Handle configuration file updates |
| |
| Usage: etc-update [options] [paths to scan] |
| |
| If no paths are specified, then \${CONFIG_PROTECT} will be used. |
| |
| Options: |
| -d, --debug Enable shell debugging |
| -h, --help Show help and run away |
| -p, --preen Automerge trivial changes only and quit |
| -q, --quiet Show only essential output |
| -v, --verbose Show settings and such along the way |
| -V, --version Show version and trundle away |
| |
| --automode <mode> |
| ${_3_HELP_TEXT} |
| ${_5_HELP_TEXT} |
| ${_7_HELP_TEXT} |
| ${_9_HELP_TEXT} |
| EOF |
| |
| [[ $# -gt 1 ]] && printf "\nError: %s\n" "${*:2}" 1>&2 |
| |
| exit ${1:-0} |
| } |
| |
| # |
| # Run the script |
| # |
| |
| declare -i count=0 |
| declare input=0 |
| declare title="Gentoo's etc-update tool!" |
| |
| PREEN=false |
| SET_X=false |
| QUIET=false |
| VERBOSE=false |
| NONINTERACTIVE_MV=false |
| while [[ -n $1 ]] ; do |
| case $1 in |
| -d|--debug) SET_X=true;; |
| -h|--help) usage;; |
| -p|--preen) PREEN=true;; |
| -q|--quiet) QUIET=true;; |
| -v|--verbose) VERBOSE=true;; |
| -V|--version) emerge --version; exit 0;; |
| --automode) parse_automode_flag $2 && shift || usage 1 "Invalid mode '$2'";; |
| -*) usage 1 "Invalid option '$1'";; |
| *) break;; |
| esac |
| shift |
| done |
| ${SET_X} && set -x |
| |
| type -P portageq >/dev/null || die "missing portageq" |
| portage_vars=( |
| CONFIG_PROTECT{,_MASK} |
| PORTAGE_CONFIGROOT |
| PORTAGE_INST_{G,U}ID |
| PORTAGE_TMPDIR |
| EROOT |
| USERLAND |
| NOCOLOR |
| ) |
| eval $(${PORTAGE_PYTHON:+"${PORTAGE_PYTHON}"} "$(type -P portageq)" envvar -v ${portage_vars[@]}) |
| export PORTAGE_TMPDIR |
| SCAN_PATHS=${*:-${CONFIG_PROTECT}} |
| |
| TMP="${PORTAGE_TMPDIR}/etc-update-$$" |
| trap "die terminated" SIGTERM |
| trap "die interrupted" SIGINT |
| |
| 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" |
| chown ${PORTAGE_INST_UID:-0}:${PORTAGE_INST_GID:-0} "${TMP}" || \ |
| die "failed to set ownership on temp dir" |
| |
| # Get all the user settings from etc-update.conf |
| cfg_vars=( |
| 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 |
| |
| [[ ${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" |
| fi |
| else |
| # NOTE: cmd_var_is_valid doesn't work with diff_command="eval emacs..." |
| # because it uses type -P. |
| if ! type ${diff_command%%[[:space:]]*} >/dev/null; then |
| die "'${diff_command}' does not seem to work, aborting" |
| fi |
| fi |
| |
| 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" |
| fi |
| fi |
| |
| if ${NONINTERACTIVE_MV} ; then |
| export mv_opts=" ${mv_opts} " |
| mv_opts="${mv_opts// -i / }" |
| fi |
| |
| if ${VERBOSE} ; then |
| for v in ${portage_vars[@]} ${cfg_vars[@]} TMP SCAN_PATHS ; do |
| echo "${v}=${!v}" |
| done |
| fi |
| |
| scan |
| |
| ${PREEN} && exit 0 |
| |
| until (( input == -1 )); do |
| if (( count == 0 )); then |
| die "Nothing left to do; exiting. :)" 0 |
| fi |
| sel_file |
| if (( input != -1 )); then |
| do_file |
| fi |
| done |
| |
| die "User termination!" 0 |