Root/
| Source at commit c757aa268ae6f1b614609ee37d4e150ea434ef8f created 3 years 29 days ago. By Luciano M. F. Rocha, extract(): fix problem where suffix to destination file was added even when not needed | |
|---|---|
| 1 | # vim: et sw=4 ts=4 filetype=sh |
| 2 | # .bashrc with functions for interactive shells |
| 3 | |
| 4 | ## doc: err message: send message to stderr |
| 5 | err() |
| 6 | { |
| 7 | local f="${FUNCNAME[1]}" |
| 8 | echo "${f:+$f: }$*" 1>&2 |
| 9 | } |
| 10 | |
| 11 | ## doc: remove a repetition of a character from the beginning of a string |
| 12 | ltrim() |
| 13 | { |
| 14 | local c s r |
| 15 | c="$1" |
| 16 | shift |
| 17 | s="$*" |
| 18 | r="${s#$c}" |
| 19 | while [ "$r" != "$s" ]; do |
| 20 | s="$r" |
| 21 | r="${s#$c}" |
| 22 | done |
| 23 | echo "$r" |
| 24 | } |
| 25 | |
| 26 | ## doc: remove a repetition of a character from the end of a string |
| 27 | rtrim() |
| 28 | { |
| 29 | local c s r |
| 30 | c="$1" |
| 31 | shift |
| 32 | s="$*" |
| 33 | r="${s%$c}" |
| 34 | while [ "$r" != "$s" ]; do |
| 35 | s="$r" |
| 36 | r="${s%$c}" |
| 37 | done |
| 38 | echo "$r" |
| 39 | } |
| 40 | |
| 41 | ## doc: remove a repetition of a character from the beginning and end of a string |
| 42 | trim() |
| 43 | { |
| 44 | local c="$1" |
| 45 | shift |
| 46 | ltrim "$c" $(rtrim "$c" $*) |
| 47 | } |
| 48 | |
| 49 | ## doc: start screen |
| 50 | s() |
| 51 | { |
| 52 | if [ -z "$STY" ]; then |
| 53 | export SCREENTERM=$TERM |
| 54 | screen -RR -D |
| 55 | else |
| 56 | err already running under screen |
| 57 | fi |
| 58 | } |
| 59 | |
| 60 | ## doc: simple http cat |
| 61 | wcat() |
| 62 | { |
| 63 | local url host port path file header |
| 64 | while [ "$1" ]; do |
| 65 | url="${1#http://}" |
| 66 | shift |
| 67 | |
| 68 | host="${url%%/*}" |
| 69 | port="${host#*:}" |
| 70 | [ "$port" = "$host" ] && port=80 |
| 71 | host="${host%%:*}" |
| 72 | path="${url#*/}" |
| 73 | if [ "$path" = "$url" ]; then |
| 74 | path="/" |
| 75 | else |
| 76 | path="/$path" |
| 77 | fi |
| 78 | file="${path##*/}" |
| 79 | [ -z "$file" ] && file="index.html" |
| 80 | echo -n "http://$host:$port$path -> $file" |
| 81 | ( |
| 82 | echo -en 'GET '$path' HTTP/1.0\r\n' |
| 83 | echo -en 'Host: '$host:$port'\r\n\r\n' |
| 84 | while read header ; do |
| 85 | [ "${#header}" -lt 3 ] && break |
| 86 | done |
| 87 | cat > "$file" |
| 88 | ) <> /dev/tcp/$host/$port 1>&0 |
| 89 | echo |
| 90 | done |
| 91 | } |
| 92 | |
| 93 | ## equals passwordcomposer |
| 94 | ## http://www.xs4all.nl/~jlpoutre/BoT/Javascript/PasswordComposer/ |
| 95 | ## doc: passwordcomposer function |
| 96 | pass() |
| 97 | { |
| 98 | local host pass |
| 99 | host="$1" |
| 100 | pass="$2" |
| 101 | |
| 102 | if [ -z "$host" ]; then |
| 103 | echo -n "Enter domain name: " |
| 104 | read host |
| 105 | fi |
| 106 | ( |
| 107 | if [ -z "$pass" ]; then |
| 108 | trap "stty echo" EXIT |
| 109 | echo -n "Enter master password (won't echo): " |
| 110 | stty -echo |
| 111 | read pass |
| 112 | stty echo |
| 113 | echo |
| 114 | fi |
| 115 | echo -n "$pass:$host" | (md5sum || md5 || openssl md5 -hex) 2>/dev/null \ |
| 116 | | cut -b 1-8 |
| 117 | ) |
| 118 | } |
| 119 | |
| 120 | ## extract archives |
| 121 | ## doc: extract archives |
| 122 | extract() |
| 123 | { |
| 124 | local arg t dir tmp abs dest |
| 125 | for arg; do |
| 126 | if ! [ -e "$arg" ]; then |
| 127 | err "$arg: doesn't exist, skipped" |
| 128 | continue |
| 129 | fi |
| 130 | |
| 131 | if [ -z "${arg%%/*}" ]; then |
| 132 | abs="$arg" |
| 133 | else |
| 134 | abs="$PWD/$arg" |
| 135 | fi |
| 136 | |
| 137 | t=$(file -i "$abs") |
| 138 | |
| 139 | if [ -z "$t" ] || [ "$t" = "application/octet-stream" ]; then |
| 140 | t="${arg##*.}" |
| 141 | fi |
| 142 | |
| 143 | if [ -z "$t" ]; then |
| 144 | err "$arg: couldn't detect type" |
| 145 | continue |
| 146 | fi |
| 147 | |
| 148 | if [ -z "${t##*text/*}" ]; then |
| 149 | err "$arg: not an archive" |
| 150 | continue |
| 151 | fi |
| 152 | |
| 153 | dest="${arg##*/}" |
| 154 | dest="${dest%.*}" |
| 155 | |
| 156 | if [ -z "$dest" ]; then |
| 157 | err "$arg: no useful name extracted" |
| 158 | continue |
| 159 | fi |
| 160 | |
| 161 | dir=".__${dest}__"; |
| 162 | |
| 163 | if [ -e "$dir" ]; then |
| 164 | err "$arg: temporary directory ($dir) already exists, remove it" |
| 165 | continue |
| 166 | fi |
| 167 | |
| 168 | mkdir -p "$dir" |
| 169 | |
| 170 | case "$t" in |
| 171 | *rpm) |
| 172 | tmp=$(rpm -qp --nosignature "$abs" --qf '%{name}-%{version}-%{release}.%{arch}') |
| 173 | mkdir -p "$dir/$tmp" |
| 174 | rpm2cpio "$abs" | (cd "$dir/$tmp" && cpio -idu --no-absolute-filenames --quiet) |
| 175 | ;; |
| 176 | *gzip) |
| 177 | if [ -z "${arg##*.tgz}" ] || [ -z "${arg##*tar*}" ]; then |
| 178 | tar -xzpsSf "$abs" -C "$dir" |
| 179 | else |
| 180 | zcat < "$abs" > "$dir/$dest" |
| 181 | fi |
| 182 | ;; |
| 183 | *bzip2) |
| 184 | if [ -z "${arg##*.tbz*}" ] || [ -z "${arg##*tar*}" ]; then |
| 185 | tar -xjpsSf "$abs" -C "$dir" |
| 186 | else |
| 187 | bzcat < "$abs" > "$dir/$dest" |
| 188 | fi |
| 189 | ;; |
| 190 | *zip) |
| 191 | unzip -q -d "$dir" "$abs" |
| 192 | ;; |
| 193 | *rar) |
| 194 | (cd "$dir" && unrar -inul x "$abs") |
| 195 | ;; |
| 196 | *iso*) |
| 197 | tmp="" |
| 198 | if isoinfo -f -R -i "$abs" &> /dev/null; then |
| 199 | tmp="-R" |
| 200 | elif isoinfo -f -J -i "$abs" &> /dev/null; then |
| 201 | tmp="-J" |
| 202 | fi |
| 203 | isoinfo $tmp -f -i "$abs" | while read; do |
| 204 | REPLY="${REPLY%;[0-9]*}" |
| 205 | REPLY="${REPLY%.}" |
| 206 | t="$dir/${REPLY%/*}" |
| 207 | if [ -f "$t" ]; then |
| 208 | rm -f "$t" |
| 209 | fi |
| 210 | mkdir -p "$t" |
| 211 | isoinfo $tmp -x "$REPLY" -i "$abs" > "$dir/$REPLY" |
| 212 | done |
| 213 | ;; |
| 214 | *) |
| 215 | err "$arg: couldn't detect type" |
| 216 | rmdir "$dir" |
| 217 | continue |
| 218 | ;; |
| 219 | esac |
| 220 | |
| 221 | tmp=("$dir/"*) |
| 222 | if ! [ -e "$tmp" ]; then |
| 223 | err "$arg: nothing extracted" |
| 224 | rmdir "$dir" |
| 225 | elif [ "${#tmp[*]}" -gt 1 ]; then |
| 226 | tmp="$dest" |
| 227 | t=1 |
| 228 | while [ -e "$tmp" ]; do |
| 229 | tmp="$dest.$((t++))" |
| 230 | done |
| 231 | mv -f "$dir" "$tmp" |
| 232 | echo "$arg: extracted to $tmp" |
| 233 | else |
| 234 | dest="${tmp##*/}" |
| 235 | t=1 |
| 236 | tmp="$dest" |
| 237 | while [ -e "$tmp" ]; do |
| 238 | tmp="$dest.$((t++))" |
| 239 | done |
| 240 | mv -f "$dir/"* "$tmp" |
| 241 | echo "$arg: extracted to $tmp" |
| 242 | rmdir "$dir" |
| 243 | fi |
| 244 | done |
| 245 | } |
| 246 | |
| 247 | ## doc: show KB size in number of CDs or DVDs |
| 248 | size_cds() |
| 249 | { |
| 250 | local size="$1" dvds cds |
| 251 | |
| 252 | ((cds=size/683593)) |
| 253 | if [ $((size%683593)) -gt 0 ]; then |
| 254 | ((cds++)) |
| 255 | fi |
| 256 | |
| 257 | ((dvds=size/4589843)) |
| 258 | if [ $((size%4589843)) -gt 0 ]; then |
| 259 | ((dvds++)) |
| 260 | fi |
| 261 | |
| 262 | echo "$cds CDs, $dvds DVDs" |
| 263 | } |
| 264 | |
| 265 | ## doc: compute expected iso size |
| 266 | size_iso() |
| 267 | { |
| 268 | local calc size arg tmp do_total |
| 269 | calc="mkisofs -J -joliet-long -r -pad -V LABEL -print-size -q" |
| 270 | if [ $# -ge 1 ]; then |
| 271 | if [ "$1" = "-c" ]; then |
| 272 | do_total=1 |
| 273 | shift |
| 274 | fi |
| 275 | for arg; do |
| 276 | tmp=$(($($calc "$arg")*2)) |
| 277 | echo "$arg: ${tmp}KB $((tmp/1024))MB: $(size_cds $tmp)" |
| 278 | ((size += tmp)) |
| 279 | done |
| 280 | if [ -n "$do_total" ]; then |
| 281 | echo "total: ${size}KB $((size/1024))MB: $(size_cds $size)" |
| 282 | fi |
| 283 | else |
| 284 | tmp=$(($($calc .)*2)) |
| 285 | echo "${tmp}KB $((tmp/1024))MB: $(size_cds $tmp)" |
| 286 | fi |
| 287 | } |
| 288 | |
| 289 | ## doc: compute expected playlist size, for DVDs |
| 290 | size_playlist() |
| 291 | { |
| 292 | perl -n -e ' |
| 293 | if (/file:/) { |
| 294 | chomp; |
| 295 | s/.*file:..//; |
| 296 | s/%(..)/chr(hex($1))/ge; |
| 297 | my $size = (stat($_))[7]; |
| 298 | $total += int($size / 2048); |
| 299 | $total++ if $size % 2048; |
| 300 | } |
| 301 | END { |
| 302 | $size = $total * 2048; |
| 303 | $size_gb = $size / 1024.0 / 1024.0 /1024.0; |
| 304 | $dvd = $total*100.0/2294921.0; |
| 305 | $free = ((100.0 - $dvd) / 100.0) * 2294921.0 / 512.0; |
| 306 | $free_t = $free * 1024.0 * 1024.0 / 16000.0; |
| 307 | $free_h = int($free_t / 3600.0); |
| 308 | $free_m = int(($free_t - $free_h * 3600.0) / 60.0); |
| 309 | $time = $free_h ? $free_h . "h" : ""; |
| 310 | $time .= $free_m ? $free_m . "m" : ""; |
| 311 | printf "sectors: \%s\n". |
| 312 | "size: \%.0f (\%.2f GB)\n". |
| 313 | "dvd: \%.2f\%\%\n". |
| 314 | "free: %.2f MB ~ %s\n", |
| 315 | $total, $size, $size_gb, $dvd, $free, $time; |
| 316 | } |
| 317 | ' "$@" |
| 318 | } |
| 319 | |
| 320 | |
| 321 | ## w/ colordiff installed |
| 322 | if type -p cdiff &> /dev/null; then |
| 323 | _use_cdiff() |
| 324 | { |
| 325 | if [ -t 1 ]; then |
| 326 | "$@" | cdiff |
| 327 | else |
| 328 | "$@" |
| 329 | fi |
| 330 | } |
| 331 | else |
| 332 | _use_cdiff() |
| 333 | { |
| 334 | if [ -t 1 ]; then |
| 335 | "$@" | $PAGER |
| 336 | else |
| 337 | "$@" |
| 338 | fi |
| 339 | } |
| 340 | fi |
| 341 | |
| 342 | ## doc: make svn more user-friendly |
| 343 | svn() { _dvcs svn "$@"; } |
| 344 | |
| 345 | ## doc: make hg more user-friendly |
| 346 | hg() { _dvcs hg "$@"; } |
| 347 | |
| 348 | ## doc: make cvs more user-friendly |
| 349 | cvs() { _dvcs cvs "$@"; } |
| 350 | |
| 351 | ## make hg/svn/alikes more user-friendly |
| 352 | _dvcs() |
| 353 | { |
| 354 | local cmd="$(type -P $1)" |
| 355 | shift |
| 356 | |
| 357 | if [ "$1" = "diff" ]; then |
| 358 | _use_cdiff "$cmd" "$@" |
| 359 | elif [ -t 1 ] && [ "$1" = "help" -o "$1" = "blame" -o "$1" = "log" ]; then |
| 360 | "$cmd" "$@" | $PAGER |
| 361 | else |
| 362 | "$cmd" "$@" |
| 363 | fi |
| 364 | } |
| 365 | |
| 366 | ## doc: colorize output of diff |
| 367 | diff() |
| 368 | { |
| 369 | _use_cdiff "$(type -P diff)" -u "$@" |
| 370 | } |
| 371 | |
| 372 | ## doc: crypt(3) |
| 373 | crypt() |
| 374 | { |
| 375 | local pass salt crypt |
| 376 | pass="$1" |
| 377 | salt="$2" |
| 378 | |
| 379 | [ -z "$pass" ] && return |
| 380 | if [ -z "$salt" ]; then |
| 381 | salt="\$1\$$(perl -e 'print map { $_ < 10 ? chr $_ + 48 : $_ < 36 ? chr $_ + 55 : chr $_ + 61 } map { int rand 62 } 1..8')\$" |
| 382 | fi |
| 383 | crypt=$(perl -e "print crypt('$pass', '$salt')") |
| 384 | echo crypt: $crypt |
| 385 | echo base64: $(perl -MMIME::Base64 -e "print encode_base64('{crypt}'.'$crypt')") |
| 386 | } |
| 387 | |
| 388 | ## run vim |
| 389 | svim() |
| 390 | { |
| 391 | _vim vim "$@" |
| 392 | } |
| 393 | |
| 394 | ## run gvim, open multiple windows for multiple files |
| 395 | mvim() |
| 396 | { |
| 397 | local a args opt nonopt fg |
| 398 | declare -a args |
| 399 | declare -a opt |
| 400 | declare -a nonopt |
| 401 | |
| 402 | fg= |
| 403 | |
| 404 | for a; do |
| 405 | if [ -z "${a##-*}" ]; then |
| 406 | [ "$a" = "-f" ] && fg=1 |
| 407 | opt=("${opt[@]}" "$a") |
| 408 | else |
| 409 | nonopt=("${nonopt[@]}" "$a") |
| 410 | fi |
| 411 | done |
| 412 | if [ -n "$fg" -o "${#nonopt[@]}" -gt 7 ]; then |
| 413 | _vim gvim "${opt[@]}" "${nonopt[@]}" |
| 414 | return |
| 415 | fi |
| 416 | |
| 417 | if [ "${#nonopt[@]}" -eq 0 ]; then |
| 418 | gvim "${opt[@]}" |
| 419 | return |
| 420 | fi |
| 421 | |
| 422 | for a in "${nonopt[@]}"; do |
| 423 | if [ -z "${a##+*}" ]; then |
| 424 | args=("${args[@]}" "$a") |
| 425 | elif [ ${#args[*]} -gt 0 ]; then |
| 426 | _vim gvim "${opt[@]}" "${args[@]}" |
| 427 | args=("$a") |
| 428 | else |
| 429 | args=("$a") |
| 430 | fi |
| 431 | done |
| 432 | if [ ${#args[*]} -gt 0 ]; then |
| 433 | _vim gvim "${opt[@]}" "${args[@]}" |
| 434 | fi |
| 435 | } |
| 436 | |
| 437 | ## smart vim: tranform file:line -> +line, file:func() -> file +/func/ |
| 438 | _vim() |
| 439 | { |
| 440 | local vi args a n t aa |
| 441 | declare -a args |
| 442 | |
| 443 | vi="$1" |
| 444 | shift |
| 445 | for a; do |
| 446 | if [ -e "$a" ] || [ -z "${a##*[0-9]:[0-9]}" ]; then |
| 447 | ## file exists, or probably an interface alias |
| 448 | ## use as is |
| 449 | args=("${args[@]}" "$a") |
| 450 | continue |
| 451 | fi |
| 452 | ## remove trailing ':', output by grep -n, for instance |
| 453 | aa="${a/%:}" |
| 454 | if [ "$aa" != "${aa%:[0-9]*}" ]; then |
| 455 | ## with line information |
| 456 | n="${aa##*:}" |
| 457 | aa="${aa%:*}" |
| 458 | ## must exist |
| 459 | if [ -e "$aa" ]; then |
| 460 | args=("${args[@]}" "$aa" "+$n") |
| 461 | else |
| 462 | args=("${args[@]}" "$a") |
| 463 | fi |
| 464 | elif [ "$aa" != "${aa%::*}" ]; then |
| 465 | ## with function |
| 466 | aa="${aa/%()}" |
| 467 | n="${aa##*::}" |
| 468 | aa="${aa%::*}" |
| 469 | aa="${aa//\\/\\\\}" |
| 470 | ## must exist |
| 471 | if [ -e "$aa" ]; then |
| 472 | args=("${args[@]}" "$aa" \ |
| 473 | '+/\v((sub|function|def)\s+<\V'"$n"'\v>)|(<'"$n"'>\s*\()') |
| 474 | else |
| 475 | args=("${args[@]}" "$a") |
| 476 | fi |
| 477 | else |
| 478 | ## use as is |
| 479 | args=("${args[@]}" "$a") |
| 480 | fi |
| 481 | done |
| 482 | command $vi "${args[@]}" |
| 483 | } |
| 484 | |
| 485 | ## doc: list directories in arguments |
| 486 | ldir() |
| 487 | { |
| 488 | local arg |
| 489 | if [ -z "${1##-*}" ]; then |
| 490 | arg="$1" |
| 491 | shift |
| 492 | fi |
| 493 | find "$@" -maxdepth 1 -type d -print0 | xargs -0r ls --color=auto -d $arg |
| 494 | } |
| 495 | |
| 496 | ## doc: list symbolic links in arguments |
| 497 | lln() |
| 498 | { |
| 499 | local arg |
| 500 | if [ -z "${1##-*}" ]; then |
| 501 | arg="$1" |
| 502 | shift |
| 503 | fi |
| 504 | find "$@" -maxdepth 1 -type l -print0 | xargs -0r ls --color=auto -dF $arg |
| 505 | } |
| 506 | |
| 507 | ## doc: test a perl program |
| 508 | perl_test() |
| 509 | { |
| 510 | ( |
| 511 | ulimit -c 0 |
| 512 | perl -Ilib -I. "$@" |
| 513 | ) 2>&1 | $PAGER |
| 514 | } |
| 515 | |
| 516 | ## doc: run the perl debugger with usual modules |
| 517 | perldbg() |
| 518 | { |
| 519 | ( |
| 520 | ulimit -c 0 |
| 521 | perl -Ilib -I. -MYAML::XS -MData::Dumper -MMIME::Base64 -d -e "$*" |
| 522 | ) |
| 523 | } |
| 524 | |
| 525 | ## doc: create a bare git clone |
| 526 | git-bare() |
| 527 | { |
| 528 | ( set +m; _git-bare-bg "$@" ) |
| 529 | } |
| 530 | |
| 531 | _git-bare-bg() |
| 532 | { |
| 533 | local url tmpd name |
| 534 | |
| 535 | tmpd=`mktemp -d` || exit 1 |
| 536 | trap "rm -fr $tmpd" EXIT |
| 537 | |
| 538 | for url; do |
| 539 | ## remove /.git |
| 540 | name=$(rtrim / "$url") |
| 541 | name="${name%.git}" |
| 542 | name=$(rtrim / "$name") |
| 543 | |
| 544 | ## remove url, add .git |
| 545 | name="${name##*/}.git" |
| 546 | |
| 547 | if [ "$name" = ".git" ]; then |
| 548 | echo "coudln't get name for $url" |
| 549 | continue |
| 550 | fi |
| 551 | |
| 552 | if [ -d "$name" ]; then |
| 553 | echo "skipping $name, already exists" |
| 554 | continue |
| 555 | fi |
| 556 | ( |
| 557 | if { git clone --bare "$url" "$name" && |
| 558 | cd "$name" && |
| 559 | git remote add -f --mirror origin "$url" || |
| 560 | { |
| 561 | git config remote.origin.url "$url" && |
| 562 | git config remote.origin.fetch "+refs/heads/*:refs/heads/*" |
| 563 | } |
| 564 | } &> $tmpd/$$; then |
| 565 | echo "finished $name" |
| 566 | else |
| 567 | echo "failed to create $name:" |
| 568 | tail -n 3 $tmpd/$$ |
| 569 | fi |
| 570 | ) & |
| 571 | done |
| 572 | wait |
| 573 | } |
| 574 | |
| 575 | ## doc: remove a line from a file: rmline <file> <line> or <file:line> |
| 576 | rmline() |
| 577 | { |
| 578 | local file line |
| 579 | |
| 580 | file="$1" |
| 581 | line="$2" |
| 582 | |
| 583 | if [ -z "$file" ]; then |
| 584 | err "missing file to remove" |
| 585 | return 1 |
| 586 | fi |
| 587 | |
| 588 | if [ -z "$line" ]; then |
| 589 | line="${file##*:}" |
| 590 | file="${file%:*}" |
| 591 | fi |
| 592 | |
| 593 | if ! [ -e "$file" ]; then |
| 594 | err "file to remove doesn't exist" |
| 595 | return 1 |
| 596 | fi |
| 597 | |
| 598 | if [ -z "$line" ]; then |
| 599 | err "missing line to remove" |
| 600 | return 1 |
| 601 | fi |
| 602 | |
| 603 | ed -s "$file" <<-EOF |
| 604 | ${line}d |
| 605 | wq |
| 606 | EOF |
| 607 | } |
| 608 | |
| 609 | ## doc: show local IP addresses |
| 610 | ips() |
| 611 | { |
| 612 | ( |
| 613 | PATH=$PATH:/usr/sbin:/sbin |
| 614 | if _exists ip; then |
| 615 | ip addr show to 0/0 |
| 616 | else |
| 617 | ifconfig | awk '/^[^ \t]/{i=$1} /inet/{ print i $0}' |
| 618 | fi |
| 619 | ) |
| 620 | } |
| 621 | |
| 622 | ## doc: list interfaces belonging to a bridge |
| 623 | brif() |
| 624 | { |
| 625 | local br="$1" |
| 626 | shift |
| 627 | |
| 628 | brctl show | awk -v br=$br ' |
| 629 | BEGIN { inbr=0 } |
| 630 | /^[^ \t]/ { inbr=0 } |
| 631 | $1 == br { |
| 632 | if ($4) { print $4 } |
| 633 | inbr=1 |
| 634 | } |
| 635 | /^[ \t]/ && inbr==1 { print $1 } |
| 636 | ' |
| 637 | } |
| 638 | |
| 639 | ## doc: list content differences between two directories |
| 640 | dirdiff() |
| 641 | { |
| 642 | local src="$1" dst |
| 643 | dst="${2:-.}" |
| 644 | |
| 645 | if [ -z "$src" ]; then |
| 646 | err "missing original directory" |
| 647 | return 1 |
| 648 | fi |
| 649 | |
| 650 | if ! [ -d "$src" ]; then |
| 651 | err "$src: not a directory" |
| 652 | return 1 |
| 653 | fi |
| 654 | if ! [ -d "$dst" ]; then |
| 655 | err "$dst: not a directory" |
| 656 | return 1 |
| 657 | fi |
| 658 | |
| 659 | diff -u <(cd "$src" && find . | LC_ALL=C sort | sed -e 's/^..//') \ |
| 660 | <(cd "$dst" && find . | LC_ALL=C sort | sed -e 's/^..//') |
| 661 | } |
| 662 | |
| 663 | ## doc: execute command with path to a binary; path added to the end or where '%' is |
| 664 | bin() |
| 665 | { |
| 666 | local p a cmd spec |
| 667 | |
| 668 | p=$(type -P "$1" 2>/dev/null) |
| 669 | if [ -z "$p" ]; then |
| 670 | err "couldn't find $1" |
| 671 | return 1 |
| 672 | fi |
| 673 | shift |
| 674 | |
| 675 | if [ $# -eq 0 ]; then |
| 676 | echo $p |
| 677 | return 0 |
| 678 | fi |
| 679 | |
| 680 | declare -a cmd |
| 681 | spec= |
| 682 | for a; do |
| 683 | if [ "$a" = "%" ]; then |
| 684 | cmd+=("$p") |
| 685 | spec=1 |
| 686 | else |
| 687 | cmd+=("$a") |
| 688 | fi |
| 689 | done |
| 690 | [ -z "$spec" ] && cmd+=("$p") |
| 691 | eval "${cmd[@]}" |
| 692 | } |
| 693 | |
| 694 | ## doc: show nfo |
| 695 | nfo() |
| 696 | { |
| 697 | local f="$1" a |
| 698 | |
| 699 | if [ -d "$f" ]; then |
| 700 | declare -a a |
| 701 | a=("$f"/*.[nN][fF][oO]) |
| 702 | f="$a" |
| 703 | fi |
| 704 | |
| 705 | if ! [ -f "$f" ]; then |
| 706 | err "couldn't find nfo in $f" |
| 707 | return 1 |
| 708 | fi |
| 709 | |
| 710 | iconv -f cp437 "$f" | tr -d '\r' | sed -e 's/^ *$//' | |
| 711 | LESS="${LESS:--}rsS" $PAGER |
| 712 | } |
| 713 | |
| 714 | |
