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