#!/bin/bash # # Copyright 2003-2005 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ # # Author: Octavio Ruiz (Ta^3) # Original Idea: Konstantin Shaposhnikov # Parts of Code by: Ed Catmur # # http://blog.tacvbo.net/data/files/yacleaner/ prog_name="yacleaner" prog_version="0.2" progam="${prog_name}-${prog_version}" set_color() { NO=$'\x1b[0;0m' BR=$'\x1b[0;01m' RD=$'\x1b[31;01m' Rd=$'\x1b[00;31m' GR=$'\x1b[32;01m' Gr=$'\x1b[00;32m' CY=$'\x1b[36;01m' Cy=$'\x1b[00;36m' YL=$'\x1b[33;01m' Yl=$'\x1b[00;33m' } set_vars() { portage_dirs=(`portageq portdir 2>/dev/null` `portageq portdir_overlay 2>/dev/null`) portage_distdir=`portageq distdir 2>/dev/null` portage_binpkgdir=`portageq pkgdir 2>/dev/null` portage_worktmpdir="`portageq envvar PORTAGE_TMPDIR 2>/dev/null`/portage" portage_logdir=`portageq envvar PORT_LOGDIR 2>/dev/null` portage_db="/var/db/pkg" } set_ifs() { oldIFS="${IFS}" IFS=' ' } reset_ifs() { IFS="${oldIFS}" } check_write_tmpdir() { tmp_dir="/tmp" check_write_tmpfiles() { if [[ ( -f ${tmp_dir}/.${prog_name}_rmlist && ! -w ${tmp_dir}/.${prog_name}_rmlist ) ]] ||\ [[ ( -f ${tmp_dir}/.${prog_name}_status && ! -w ${tmp_dir}/.${prog_name}_status ) ]]; then set_alternate_tmp else rm -f ${tmp_dir}/.${prog_name}_rmlist ${tmp_dir}/.${prog_name}_status fi } set_alternate_tmp() { if [[ -d ${HOME} ]] && [[ -w ${HOME} ]]; then tmp_dir="${HOME}" check_write_tmpfiles else echo -e "${RD}*${NO} Could not set a valid temporary directory\n" exit 1 fi } if [[ ! -d ${tmp_dir} ]] || [[ ! -w ${tmp_dir} ]]; then set_alternate_tmp else check_write_tmpfiles fi } check_for_color() { if [[ ! ${option_color} == no ]]; then set_color fi } verbose_output() { du -h --max-depth=0 ${files2delete[@]} 2>/dev/null | \ awk '{ print $2, s, $1}' | \ sed -r "s:.*\s:${Gr}\\0${NO}:" | \ while read i; do echo -e " [ ${Cy}${1}${NO} ]\t$i" done } show_help() { check_for_color help_message=${1} shift display_help_args="$*" if [[ -n ${help_message} ]]; then cat < ${CY}${progam} --help${NO} ${BR}Options:${NO} [ ${GR}--ask${NO} | ${GR}--nocolor${NO} | ${GR}--pretend${NO} | ${GR}--verbose${NO} ] ${BR}Actions:${NO} [ ${GR}--delete${NO} ] END if [[ $help_more != 1 ]]; then cat <> ${tmp_dir}/.${prog_name}_status else set_ifs echo ${files2delete[@]} | \ while read files; do echo rm -rf \""$(eval echo "\$portage_${1}dir")/$files"\" >> ${tmp_dir}/.${prog_name}_rmlist done reset_ifs cd $(eval echo \$portage_${1}dir) eval t_${1}_kb=$(du -ck ${files2delete[@]} 2>/dev/null | tail -n 1 | cut -f1) echo "${GR}*${NO} $(eval echo \$portage_${1}dir) cleaned." >> ${tmp_dir}/.${prog_name}_status if [[ -n ${option_verbose} ]]; then verbose_output ${1} else echo -e " [ ${Cy}${1}${NO} ] \t\t $(eval echo \$t_${1}_kb) K" fi fi } get_total() { t_mb=$(( $((${t_binpkg_kb:-0} + ${t_dist_kb:-0} + ${t_log_kb:-0} + ${t_worktmp_kb:-0}))/1024 )) echo -e "\n [ ${RD}total${NO} ] \t\t ${BR}${t_mb} M${NO}" } upper() { echo "$*" | tr [[:lower:]] [[:upper:]] } finish_clean() { echo cat "${tmp_dir}/.${prog_name}_status" rm -f ${tmp_dir}/.${prog_name}_rmlist ${tmp_dir}/.${prog_name}_status echo exit 0 } query_user() { prompt="${BR}$1${NO} " shift rprompt="[${GR}Yes${NO}/${RD}No${NO}] " responses=( Yes No ) echo -en "\n$prompt" # Ctrl-C trap "echo 'Interrupted.' >&2; exit 0" INT while true; do if read -e -p "$rprompt"; then for ((i=0; i<${#responses[@]}; ++i)); do response="${responses[$i]}" [[ "$REPLY" == "${response:0:${#REPLY}}" ]] \ && return $i done for ((i=0; i<${#responses[@]}; ++i)); do response="${responses[$i]}" [[ "$(upper "$REPLY")" == "$(upper "${response:0:${#REPLY}}")" ]] \ && return $i done echo "Sorry, response '$REPLY' not understood." >&2 echo -en "\n$prompt" else # Ctrl-D echo "Interrupted." >&2 exit 0 fi done trap - INT } ask_remove_files() { [[ ! -f ${tmp_dir}/.${prog_name}_rmlist ]] && ask_pretend="nothing" case $ask_pretend in nothing ) echo -e "${BR}\nNothing to delete${NO}" ; finish_clean;; pretend ) finish_clean;; ask ) while :; do query_user "Do you want me to delete these old${deleargs:- }files?" case $? in 0 ) remove_files && break ;; 1 ) exit 0 ;; esac done;; * ) remove_files;; esac } remove_files(){ trap "echo '${RD}X${NO}' >&2; exit 0" INT echo -e "\n>>> Waiting 5 seconds before starting..." echo ">>> (Control-C + Enter to abort)..." echo -n ">>> Cleaning in " for seconds in $(seq 1 5); do echo -n "${RD}${seconds}${NO} " sleep 1 done echo trap - INT echo -e "\n>>> Deleting old${deleargs:- }files..." while :; do [[ "$(whoami)" == "root" ]] || echo -en "\nPlease enter your root " su -c 'cat '"${tmp_dir}"'/.'"${prog_name}"'_rmlist | sh' case $? in 0) finish_clean return 0;; 1) continue;; *) exit 1;; esac done } query_the() { set_ifs if [[ -z ${set_vars_once} ]]; then set_vars_once="1" set_vars fi if [[ ${1} == "dist" ]] || [[ ${1} == "binpkg" ]] && [[ -z ${query_db_once} ]]; then query_db_once="1" dbpkg_files="` find ${portage_db} -mindepth 2 -maxdepth 2 | \ while read ipkg do echo ${ipkg/${portage_db}\//} done`" elif [[ ${1} == "log" ]]; then : elif [[ ${1} == "worktmp" ]]; then : fi reset_ifs } c_log() { ls -1 ${portage_logdir} 2>/dev/null } c_worktmp() { ls -1 ${portage_worktmpdir} | grep -v homedir 2>/dev/null } i_dist() { set_ifs echo $dbpkg_files | \ while read db do for (( N_portage_dirs=$((${#portage_dirs[@]} - 1)) ; N_portage_dirs>=0; N_portage_dirs-- )) do cat ${portage_dirs[$N_portage_dirs]}/${db/-[0-9]*/}/files/digest-${db##*/} 2>/dev/null done done | \ awk '{print $3}' reset_ifs } c_dist() { ls -1 ${portage_distdir} | grep -v cvs-src 2>/dev/null } i_binpkg() { echo $dbpkg_files | \ while read db do echo All/${db##*/}.tbz2 echo ${db}.tbz2 done } c_binpkg() { find $portage_binpkgdir -type f -follow |\ while read binpkg do echo ${binpkg/$portage_binpkgdir\//} done } compare_lists() { set_ifs ${1} | sort >| ${tmp_dir}/.${prog_name}_${1} ${2} | sort >| ${tmp_dir}/.${prog_name}_${2} files2delete=(`comm ${3} ${tmp_dir}/.${prog_name}_${1} ${tmp_dir}/.${prog_name}_${2}`) rm -f ${tmp_dir}/.${prog_name}_${1} ${tmp_dir}/.${prog_name}_${2} reset_ifs } for ((i=$OPTIND; i<=$#; ++i)); do case ${!i} in -a | --ask ) ask_pretend="ask";; -d | --delete ) action_delete="yes";; -h | --help ) option_help="yes";; -nc | --nocolor ) option_color="no";; -p | --pretend ) ask_pretend="pretend";; -v | --verbose ) option_verbose="yes";; -V | --version ) option_version="yes";; binpkg ) delete_argument="binpkg ${delete_argument}" ;; dist ) delete_argument="dist ${delete_argument}" ;; log ) delete_argument="log ${delete_argument}" ;; worktmp ) delete_argument="worktmp ${delete_argument}" ;; all ) delete_argument="binpkg dist log worktmp" ;; * ) OPTIND=$i; should_exit="yes" ; show_help "${!i} is an invalid option"; break ;; esac done check_for_color if [ $# -eq "0" ]; then show_help "please tell me what to do." elif [[ -n "${option_help}" ]]; then show_help elif [[ -n "${option_version}" ]]; then print_version exit 0 fi if [[ -n "${action_delete}" ]]; then action_delete="yes" fi if [[ $# -ge "1" ]] && \ [[ -z "${delete_argument}" ]] && \ [[ -z "${option_help}" ]] && \ [[ ! "${should_exit}" = "yes" ]]; then show_help "please tell me what you want to delete." elif [[ -n "${option_help}" ]]; then show_help "" "extended" "1" else if [[ -z "${printed_help}" ]] && [[ ! "${should_exit}" = "yes" ]]; then check_write_tmpdir delete_argument=($(for d_args in `echo ${delete_argument}`; do echo ${d_args}; done | sort -u)) [[ "${#delete_argument[@]}" == 1 ]] && deleargs=" ${delete_argument} " echo -e "\n${Gr}These are the old${deleargs:- }files that I would delete:${NO}\n" for (( N_arguments=$((${#delete_argument[@]} - 1 )) ; N_arguments>=0 ; N_arguments-- )) do query_the ${delete_argument[$N_arguments]} pretendlist_of ${delete_argument[$N_arguments]} done get_total ask_remove_files fi fi if [[ -n "${printed_help}" ]]; then eval print_help ${display_help_args} exit 0 fi