From c37f78246606080265b31eff6aad444a49654d34 Mon Sep 17 00:00:00 2001 From: Q Date: Sun, 11 Jul 2021 21:58:42 +0300 Subject: [PATCH] changing principle of cclip --- files/cclip | 455 +++++++++++++++++++----------------------- files/file_version.py | 11 +- 2 files changed, 211 insertions(+), 255 deletions(-) diff --git a/files/cclip b/files/cclip index 057ffd0..16fc6e6 100755 --- a/files/cclip +++ b/files/cclip @@ -5,35 +5,36 @@ _help() { SELF=$( basename "$0" ) echo "Console Clipboard manager - Usage: $SELF [command] [clipboard name] [filename] + Note: Copying happens only to a list. If source file is deleted, it + can not be copied anymore. - Short Command Description - c copy Copy from file / stdin - x cut Move file to clipboard - p paste Paste to file / stdout - o move Paste to file, by moving (clipboard deleted) - d delete Delete an entry - D Delete Clear the whole clibboard - e edit Edit contents, with \$EDITOR - l list [default] List names of clipboards - pmenu pastemenu Paste a file/folder using a simple menu (ex. for MC) - autocomplete Get Bash autocompletion script + List clipboard names: + $SELF l/list - Clipboard Name: Any string for the clipboard name. default: 0 - Filename: - If not given, defaults to clipboard name - - uses stdout/stdin - When reading from clipboard: - File or folder to write the clipboard contents. If omitted: stdout - When writing to clipboard: - File or folder to read from. If omitted: stdin + Make a link to clipboard: + $SELF c/copy filename[s] - Shorthand: - echo my data | $SELF 1 # writes data with name: 1 - $SELF 1 | cat - # prints the contents of file: 1 + 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 path, + CCLIP_HOME environment variable sets clipboard storage folder, defauls to ~/.cache/cclip " exit @@ -43,9 +44,8 @@ _help() { _load_config() { STORAGE=${CCLIP_HOME:-~/.cache/cclip} [[ -d "$STORAGE" ]] || { - _msg "Creating $STORAGE folder, and inserting sample data" + _msg "Creating $STORAGE folder" mkdir -p "$STORAGE" - echo Sample data | _write_stdin 0 } } @@ -55,30 +55,40 @@ _list() { _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%1s %5s %10s %s%s\n" "$he" T Size Added Name "$no" + printf "%s%-${longest}s %-4s %-8s %s%s\n" "$he" Name Size Added Path "$no" IFS=$'\n' while read n; do - size=$( du -k -h -s --apparent-size "$n" | awk '{ print $1 }' ) - type=$( stat --printf "%A" "$n" ) - type=${type:0:1} - if [[ "$type" = "-" ]]; then type=f; fi + 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:0:8}" + date="${date:2:8}" fi - printf "%1s %5s %10s %s%s%s\n" \ - "${type}" \ + + printf "%-${longest}s %-4s %-8s %s%s%s\n" \ + "${n}" \ "$size" \ "${date}" \ - "$hl" "$n" "$no" + "$hl" "${f}" \ + "$no" done < <( ls -t ) } @@ -86,137 +96,156 @@ _simple_list() { ls -1 "$STORAGE" } - -_write() { - # no file mentioned, use stdin - [[ -z "$FILE" ]] && stream_in=1 - # no file mentioned, use the name as file - [[ -z "$FILE" ]] && { - [[ -e "$NAME" ]] && { - # NAME is actually the file, reverse roles - FILE="$NAME" - NAME=$( basename "$NAME" ) - } - } - [[ -e "$FILE" ]] && { - # Input file exists, dont use stream - stream_in=0 - } - - [[ $stream_in -ne 1 ]] && [[ -z "$FILE" ]] && { - _msg File to read needed, or use stdin - exit 1 - } - - # sure to write something, delete target first - rm -rf "$STORAGE/$NAME" - - [[ "$stream_in" -eq 1 ]] && { - _write_stdin "$NAME" - } || { - _write_files "$NAME" "$FILE" - } -} - -_write_files() { # name, file - if [[ "$1" = "$2" ]]; then - to="" - else - to=" to $1" - fi - if [[ $MOVE = true ]]; then - echo Cutting "${2}$to" - mv "$2" "$STORAGE/$1" - else - echo Copying "${2}$to" - cp -aT "$2" "$STORAGE/$1" - fi - touch -h "$STORAGE/$1" -} - -_write_stdin() { # name - cat - > "$STORAGE/$1" -} - -_read_menu() { - pushd "$STORAGE" &>/dev/null - echo "Select clipboard from menu" - ls - read -e NAME - popd &>/dev/null - NAME=${NAME#\'} - NAME=${NAME#\"} - NAME=${NAME%\'} - NAME=${NAME%\"} - if [[ -e "$STORAGE/$NAME" || -L "$STORAGE/$NAME" ]]; then - NAME=$( basename "$NAME" ) - _read - else - echo "Could not find '$NAME'" - fi -} - - -_read() { - [[ -e "$STORAGE/$NAME" || -L "$STORAGE/$NAME" ]] || { - _msg "No such clipboard: '$NAME'" - return - } - [[ "$FILE" = "-" ]] && stream_out=1 - [ -t 1 ] || stream_out=1 - [[ "$stream_out" -eq 1 ]] && { - _read_stdout "$NAME" - return - } - # no file mentioned, use the name as file - [[ -z "$FILE" ]] && { - [[ -n "$NAME" ]] && { - FILE=$( basename "$NAME" ) - } - [[ -e "$FILE" ]] && { - echo "'$FILE' already exists, not overwriting" - exit 1 - } - } - _read_files "$NAME" "$FILE" -} - - -_read_files() { # name, file - if [[ "$1" = "$2" ]]; then - to="" - else - to=" to $2" - fi - if [[ $MOVE = true ]]; then - echo Moving "${1}$to" - mv "$STORAGE/$1" "$2" - else - echo Pasting "${1}$to" - cp -aT "$STORAGE/$1" "$2" - fi -} - -_read_stdout() { # name +_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 - [[ -e "$STORAGE/$1" || -L "$STORAGE/$1" ]] || { - _msg No such clipboard - exit 1 - } - [[ "$FORCE" -ne 1 ]] && { - read -p "Really delete '$1' ? Break to cancel. " foo - } - echo Deleting "$1" - rm -rf "$STORAGE/$1" + + 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 } @@ -225,37 +254,9 @@ _delete_all() { } _msg() { - echo "$@" >&2 + echo "$@" } -_get_name() { - [[ "$ARG1" = "$CMD" ]] && { - NAME="$ARG2" - } || { - NAME="$ARG1" - } - [[ -z "$NAME" ]] && NAME=0 - return 0 -} - -_get_file() { - [[ "$ARG1" = "$NAME" ]] && { - FILE="$ARG2" - } - [[ "$ARG2" = "$NAME" ]] && { - FILE="$ARG3" - } - return 0 -} - -_edit() { - EDITOR=${EDITOR:-vi} - [[ -f "$STORAGE/$NAME" ]] || { - _msg "$NAME is a directory, can not edit" - exit 1 - } - $EDITOR "$STORAGE/$NAME" -} _get_completer() { self=$( basename $( readlink -f "$0" ) ) @@ -263,23 +264,21 @@ _get_completer() { local curr_arg curr_arg=${COMP_WORDS[COMP_CWORD]} if [[ $COMP_CWORD -eq 1 ]]; then - COMPREPLY=( $(compgen -W "help autocomplete l list c copy x cut p paste o move delete Delete pmenu" -- $curr_arg ) ); + 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 - o|move|p|paste|d*) + p|paste|d*|rm|remove) local IFS=$'"'"'\n'"'"' local remotelist=( $( eval CCLIP_EXEC simplelist ) ) COMPREPLY=( $(compgen -W "${remotelist[*]}" -- $curr_arg ) ); ;; - x|c*) - COMPREPLY=( $(compgen -f -d -- $curr_arg ) ); + c*) + COMPREPLY=( $(compgen -f -d -X '' -- $curr_arg ) ); ;; esac fi -if [[ $COMP_CWORD -eq 3 ]]; then - COMPREPLY=( $(compgen -f -d -- $curr_arg ) ); -fi + } complete -F _CCLIP_EXEC_complete CCLIP_EXEC # Run me as: source <( CCLIP_EXEC autocomplete ) @@ -294,68 +293,16 @@ for (( i=1; i<=$#; i++ )); do done _load_config source qolop &>/dev/null || true -ARG1="$1" -ARG2="$2" -ARG3="$3" -MOVE=false -CMD=help -if [[ -z "$1" ]]; then CMD=list; fi -[[ "$1" = "p" || "$1" = "paste" ]] && { CMD=read; ARG1=$CMD; } -[[ "$1" = "o" || "$1" = "move" ]] && { CMD=read; ARG1=$CMD; MOVE=true; } -[[ "$1" = "pmenu" || "$1" = "pastemenu" ]] && { CMD=read_menu; ARG1=$CMD; } -[[ "$1" = "c" || "$1" = "copy" ]] && { CMD=write; ARG1=$CMD; } -[[ "$1" = "x" || "$1" = "cut" ]] && { CMD=write; ARG1=$CMD; MOVE=true; } -[[ "$1" = "d" || "$1" = "delete" || "$1" = "del" ]] && { CMD=delete; ARG1=$CMD; } -[[ "$1" = "D" || "$1" = "Delete" || "$1" = "Del" ]] && { CMD=delete_all; ARG1=$CMD; } -[[ "$1" = "l" || "$1" = "list" ]] && { CMD=list; ARG1=$CMD; } -[[ "$1" = "e" || "$1" = "edit" ]] && { CMD=edit; ARG1=$CMD; } -[[ "$1" = "h" || "$1" = "help" ]] && _help +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; } -[[ -n "$1" ]] && [[ -e "$STORAGE"/"$1" ]] && CMD=read -# if stdout redirected, default to read -[ -t 1 ] || CMD=read -# if stdin comes from stream, default to write -[ -t 0 ] || CMD=write - -[[ "$CMD" = help ]] && { - _help - exit $? -} -[[ "$CMD" = list ]] && { - _list - exit $? -} -[[ "$CMD" = delete_all ]] && { - _delete_all - exit $? -} -_get_name -_get_file -[[ "$CMD" = read ]] && { - _read - exit $? -} -[[ "$CMD" = read_menu ]] && { - _read_menu - exit $? -} -[[ "$CMD" = delete ]] && { - _delete "$NAME" - exit $? -} -[[ "$CMD" = write ]] && { - _write - exit $? -} -[[ "$CMD" = edit ]] && { - _edit - exit $? -} - - -# TODO: -# folders written as TAR -# content encrypted with conf variable? - +exit $EC diff --git a/files/file_version.py b/files/file_version.py index 9fe0fe5..815184a 100755 --- a/files/file_version.py +++ b/files/file_version.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os, argparse, sys from shutil import copyfile, copytree from datetime import datetime @@ -35,6 +35,13 @@ def get_options(): help = "Quiet operation", default = False ) + parser.add_argument( + '-n', + action = "store_true", + dest = "no_action", + help = "Do not modify files, just print the new file name.", + default = False + ) parser.add_argument(action = "store", dest = "file") return parser.parse_args() @@ -111,6 +118,8 @@ if __name__ == "__main__": new_name = get_version_name(opts.file) if not opts.quiet: print(new_name) + if opts.no_action: + sys.exit(0) test_existing(new_name, opts.force) if opts.move: os.rename(opts.file, new_name)