#!/bin/bash set -e _help() { SELF=$( basename "$0" ) echo "Console Clipboard manager Note: Copying happens only to a list. If source file is deleted, it can not be copied anymore. List clipboard names: $SELF l/list Make a link to clipboard: $SELF c/copy filename[s] Paste files using clipboard: $SELF p/paste [switches] clipboardname[s] Switches: -l Use hardlink instead of file copying -k Keep clipboard link -m Move instead of copy (can not be used with -h, can not -k) -a Paste all files from clipboard Delete files using clipboard links: $SELF rm/remove clipboardname[s] Clear clipboard links: $SELF d/del clipboardname[s] Clear all clipboard links: $SELF D/Del clipboardname[s] Get autocomplete: $SELF autocomplete Config: CCLIP_HOME environment variable sets clipboard storage folder, defauls to ~/.cache/cclip " exit } _load_config() { STORAGE=${CCLIP_HOME:-~/.cache/cclip} [[ -d "$STORAGE" ]] || { _msg "Creating $STORAGE folder" mkdir -p "$STORAGE" } } _list() { cd "$STORAGE" [[ $(ls -A) ]] || { _msg "No clipboard entries" return } longest=4 for n in *; do if [[ $longest -lt ${#n} ]]; then longest=${#n} fi done hl=$( _qCol W 2>/dev/null || true ) he=$( _qCol y U 2>/dev/null || true ) no=$( _qCol z 2>/dev/null || true ) now=$( date -Idate ) printf "%s%-${longest}s %-4s %-8s %s%s\n" "$he" Name Size Added Path "$no" IFS=$'\n' while read n; do f=$( _get_item "$n" ) size=$( du -k -h -s --apparent-size "$f" | awk '{ print $1 }' ) #type=$( stat --printf "%A" "$f" ) #type=${type:0:1} #if [[ "$type" = "-" ]]; then type=f; fi date=$( stat --printf "%y" "$n" ) if [[ "$date" = "$now"* ]]; then date=${date#* } date="${date:0:8}" else date=${date%% *} date="${date:2:8}" fi printf "%-${longest}s %-4s %-8s %s%s%s\n" \ "${n}" \ "$size" \ "${date}" \ "$hl" "${f}" \ "$no" done < <( ls -t ) } _simple_list() { ls -1 "$STORAGE" } _get_item() { # TODO: Items could be just symbolic links cat "$STORAGE/$1" } _copy() { if [[ $# -lt 2 ]]; then _help fi for (( i=2; i<=$#; i++ )); do name=$( basename "${!i}" | tr -c '[:alnum:],_.\n\r' "-" ) origname="$name" path=$( readlink -f "${!i}" ) if [[ -d "$path" ]]; then path="$path/" fi post=0 while [[ -e "$STORAGE/${name}" ]]; do post=$(( post + 1 )) name="${origname}-${post}" done printf "%s %s\n" "$name" "$path" printf "%s" "$path" > "$STORAGE/${name}" done } _paste() { if [[ $# -lt 2 ]]; then _help fi for (( i=2; i<=$#; i++ )); do case ${!i} in -a) copy_all=1; ;; -l) hardlink=1; ;; -k) keeplink=1; ;; -m) movelink=1; ;; esac done if [[ "$copy_all" = 1 ]]; then for n in "$STORAGE"/*; do _paste_single "$( basename "$n" )" done return fi for (( i=2; i<=$#; i++ )); do if [[ "${!i}" =~ ^-.$ ]]; then continue fi _paste_single "${!i}" done } _paste_single() { if [[ ! -e "$STORAGE/$1" ]];then _msg "No such clipboard: '$1'" return fi path=$( _get_item "$1" ) if [[ ! -e "$path" ]]; then _msg "No such path: $1 -> '$path'" return fi target=$( basename "$path" ) if [[ -d "$path" ]]; then extension="" basetarget="${target}" else if [[ "$target" = *.* ]]; then basetarget="${target%.*}" extension=."${target##*.}" else extension="" basetarget="${target}" fi fi origbase="$basetarget" post=0 while [[ -e "${basetarget}${extension}" ]]; do post=$(( post + 1 )) basetarget="${origbase}-${post}" done target="${basetarget}${extension}" if [[ "$movelink" = 1 ]]; then keeplink=0 echo Moving "${path} -> $target" mv "$path" "$target" || return else hardlinkswitch="" if [[ "$hardlink" = 1 ]]; then hardlinkswitch="-l" fi echo Copying "${path} -> $target" cp -aT $hardlinkswitch "${path}" "$target" || return fi if [[ ! "$keeplink" = 1 ]]; then echo "Removing link: $1" rm "$STORAGE/$1" fi } _remove() { if [[ $# -lt 2 ]]; then _help fi for (( i=2; i<=$#; i++ )); do if [[ ! -e "$STORAGE/${!i}" ]];then _msg "No such clipboard: '${!i}'" continue fi path=$( _get_item "${!i}" ) if [[ ! -e "$path" ]]; then _msg "No such path: $1 -> '$path'" continue fi echo "Removing data: ${path}" rm -ir "$path" echo "Removing link: ${!i}" rm "$STORAGE/${!i}" done } _delete() { # name for (( i=2; i<=$#; i++ )); do name=$( basename "${!i}" ) if [[ ! -e "$STORAGE/$name" ]]; then _msg "No such clipboard '$name'" EC=1 continue fi printf "Removing %s: %s\n" "$name" $( _get_item "$name" ) rm "$STORAGE/$name" done } _delete_all() { [[ -e "$STORAGE" ]] || { exit 0 } _simple_list [[ "$FORCE" -ne 1 ]] && { read -p "Really delete the whole clipboard? ($STORAGE) Break to cancel. " foo } rm -vrf "$STORAGE/" mkdir "$STORAGE" } _msg() { echo "$@" } _get_completer() { self=$( basename $( readlink -f "$0" ) ) echo '_CCLIP_EXEC_complete() { local curr_arg curr_arg=${COMP_WORDS[COMP_CWORD]} if [[ $COMP_CWORD -eq 1 ]]; then COMPREPLY=( $(compgen -W "help autocomplete l list c copy p paste delete Delete" -- $curr_arg ) ); fi if [[ $COMP_CWORD -eq 2 ]]; then case ${COMP_WORDS[$(( $COMP_CWORD - 1 ))]} in p|paste|d*|rm|remove) local IFS=$'"'"'\n'"'"' local remotelist=( $( eval CCLIP_EXEC simplelist ) ) COMPREPLY=( $(compgen -W "${remotelist[*]}" -- $curr_arg ) ); ;; c*) COMPREPLY=( $(compgen -f -d -X '' -- $curr_arg ) ); ;; esac fi } complete -F _CCLIP_EXEC_complete CCLIP_EXEC # Run me as: source <( CCLIP_EXEC autocomplete ) ' | sed "s,CCLIP_EXEC,$self,g" exit 0 } for (( i=1; i<=$#; i++ )); do [[ "${!i}" = "-h" ]] && _help [[ "${!i}" = "--help" ]] && _help done _load_config source qolop &>/dev/null || true CMD="${1:-list}" [[ "$CMD" = help || "$CMD" = "-h" || "$CMD" = "--help" ]] && { _help; } [[ "$CMD" = "l" || "$CMD" = "list" ]] && { _list; } [[ "$CMD" = "c" || "$CMD" = "copy" ]] && { _copy "$@"; } [[ "$CMD" = "d" || "$CMD" = "delete" || "$CMD" = "del" ]] && { _delete "$@"; } [[ "$CMD" = "D" || "$CMD" = "Delete" || "$CMD" = "Del" ]] && { _delete_all; } [[ "$CMD" = "p" || "$CMD" = "paste" ]] && { _paste "$@"; } [[ "$CMD" = "rm" || "$CMD" = "remove" ]] && { _remove "$@"; } [[ "$1" = "autocomplete" ]] && { _get_completer; exit; } [[ "$1" = "simplelist" ]] && { _simple_list; exit; } exit $EC