Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 124 additions & 37 deletions virn
Original file line number Diff line number Diff line change
@@ -1,48 +1,135 @@
#!/usr/bin/env bash
#!/bin/sh
# virn, Copyright (C) 2007-2010 by Jonas Kramer. All rights reserved.
# Published under the terms of the GNU General Public License (GPL).

# This is a more portable BASH remake of my old script "vimove" which depended
# on ZSH.
_usage()
{
printf "%s\\n" "Usage: $(expr "${0}" : '.*/\([^/]*\)') [options] file ..." >&2
printf "%s\\n" "Rename lots of files using your favorite text editor." >&2
printf "\\n" >&2
printf "%b\\n" " -e, --editor use this editor instead of the default \$EDITOR" >&2
printf "%b\\n" " -h, --help show this help message and exit" >&2
exit 1
}

die () {
echo "$@" >&2
exit -1
_die()
{
[ -z "${1}" ] && return 1
printf "%b\\n" "$(expr "${0}" : '.*/\([^/]*\)'): ${*}" >&2
exit 1
}

# Verify that there's an $EDITOR defined.
[ -z "$EDITOR" ] && die '$EDITOR not defined.'
_mkdir_p()
{ #portable mkdir -p function
for _mkdir_p__dir; do
_mkdir_p__IFS="$IFS"
IFS="/"
set -- $_mkdir_p__dir
IFS="$_mkdir_p__IFS"
(
case "$_mkdir_p__dir" in
/*) cd /; shift ;;
esac
for _mkdir_p__subdir; do
[ -z "${_mkdir_p__subdir}" ] && continue
if [ -d "${_mkdir_p__subdir}" ] || mkdir "${_mkdir_p__subdir}"; then
if cd "${_mkdir_p__subdir}"; then
:
else
printf "%s\\n" "_mkdir_p: Can't enter ${_mkdir_p__subdir} while creating ${_mkdir_p__dir}"
exit 1
fi
else
exit 1
fi
done
)
done
unset _mkdir_p__dir
}

# Exit before doing anything if there are no arguments anyway.
ARGV=("$@")
[ $# -eq 0 ] && exit
_dirname()
{ #return string containing dirname on success, 1 on failure
[ -z "${1}" ] && return 1

# Create targets file.
TARGETS="/tmp/virn-$RANDOM"
for FILE in "$@"; do echo "$FILE" >> $TARGETS; done

# Start the $EDITOR so we can edit our file names.
if $EDITOR $TARGETS; then
FILENO=0
COUNT="`wc -l $TARGETS | sed -nre 's/^[ \t]*([0-9]+).*$/\1/p'`"

# Exit with error message if number of arguments and line count of
# targets file differ.
if [ "$COUNT" -ne "$#" ]; then
rm $TARGETS
die 'Numbers of files mismatch.'
fi

# Read targets file line by line and rename the source
# files from ARGV appropriately.
while read TARGET; do
SOURCE="${ARGV[$FILENO]}"
if [ "$SOURCE" != "$TARGET" ]; then
mv -f -- "$SOURCE" "$TARGET"
fi
FILENO=$[$FILENO+1]
done < $TARGETS
#http://www.linuxselfhelp.com/gnu/autoconf/html_chapter/autoconf_10.html
case "${1}" in
/*|*/*) _dirname__dir=$(expr "x${1}" : 'x\(.*\)/[^/]*' \| '.' : '.')
printf "%s\\n" "${_dirname__dir}" ;;
*) printf "%s\\n" ".";;
esac
}

if [ ! -t 0 ]; then
#there is input comming from pipe or file, add to the end of $@
set -- "${@}" $(cat)
fi

rm $TARGETS
[ "${#}" -eq "0" ] && _usage

for arg; do #parse options
case "${arg}" in
-h|--help) _usage ;;
-e|--editor)
if [ "${#}" -gt "1" ]; then
editor="$(printf "%s " "${@}" | awk '{print $2}')"
shift && shift
else
_die "Option \`${arg}' requires a parameter"
fi ;;
-*) printf "%s\\n" "$(expr "${0}" : '.*/\([^/]*\)'): unrecognized option \`${arg}'" >&2; _usage ;;
esac
done

[ -z "${editor}" ] && editor="${EDITOR}"
[ -z "${EDITOR}" ] && _die "\${EDITOR} not defined."

# Create targets file.
targets="/tmp/virn-${$}"
i=0; for FILE; do
[ -e "${FILE}" ] || {
i="$((i+1))";
printf "%s\\n" "Warning: '${FILE}' doesn't exist, skipping ..." >&2 ;
continue; }
printf "%s\\n" "${FILE}" >> "${targets}"
done

[ ! -f "${targets}" ] && exit

# Start $EDITOR
if ${editor} ${targets}; then
[ ! -t 0 ] && reset
count="$(awk 'END {print NR}' "${targets}" 2>/dev/null)"
param_len="${#}"; param_len="$((param_len - i))"

# Exit with error message if number of arguments and line count of
# targets file differ.
if [ "${count}" -ne "${param_len}" ]; then
rm -- "${targets}"
_die 'Numbers of files mismatch.'
fi

# Read targets file line by line and rename the source
# files from ARGV appropriately.
while read target; do
while [ ! -e "${source}" ]; do
source="${1}"
shift
done
if [ X"${source}" != X"${target}" ]; then
case "${target}" in
/*|*/*)
target="$(printf "%s" "${target}" | \
sed "s:\$HOME:$HOME:g;s:~/:$HOME/:;")"
_mkdir_p "$(_dirname "${target}")" ;;
esac
mv -v -f -- "${source}" "${target}"
case "${source}" in
/*|*/*) rmdir -p "$(_dirname "${source}")" 2>/dev/null ;;
esac
fi
source=""
done < "${targets}"
fi

rm "${targets}"