commit cefd40b6dcdb1b45037189893a858e6895906a6c Author: Alexander Gehrke Date: Mon Sep 30 14:49:14 2019 +0200 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..4943522 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# crater's script collection + +Some scripts that I wrote over the years and wanted to store with versioning +somewhere. Maybe some of them are useful for others. + +Documentation may or may not follow, some scripts include a `--help` output. diff --git a/bin/alert b/bin/alert new file mode 100755 index 0000000..b0fa120 --- /dev/null +++ b/bin/alert @@ -0,0 +1,3 @@ +#!/bin/zsh +time=$1; shift +echo "DISPLAY=:0.0 sm \"$@\"" | at $time diff --git a/bin/autoless b/bin/autoless new file mode 100755 index 0000000..486099e --- /dev/null +++ b/bin/autoless @@ -0,0 +1,14 @@ +#!/bin/zsh +if [[ -n "$@" ]]; then + out=$(<"$@") +else + out=$(cat) +fi +((lim=$LINES-1)) +numlines=$(echo $out | wc -l) +if (( $numlines > $lim )) +then + echo $out | less -R +else + echo $out +fi diff --git a/bin/dbus-find b/bin/dbus-find new file mode 100755 index 0000000..fa634e3 --- /dev/null +++ b/bin/dbus-find @@ -0,0 +1,17 @@ +#!/bin/zsh + +if [[ $1 == "-u" ]]; then + USER=$2 + SUDO="sudo -u $USER" + shift 2 +else + SUDO= +fi + +export "$({for i in $(pgrep -u $USER dbus-daemon); grep -z DBUS_SESSION_BUS_ADDRESS /proc/$i/environ} | head -n 1)" +if [[ -z $DBUS_SESSION_BUS_ADDRESS ]]; then + echo "No DBUS found" + exit 1 +else + exec $=SUDO "$@" +fi diff --git a/bin/dmjavadoc b/bin/dmjavadoc new file mode 100755 index 0000000..1c3b1e1 --- /dev/null +++ b/bin/dmjavadoc @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby + +require 'dmenu' + +JAVA_DOC_LOCATION = "/usr/share/doc/api" +JAVAFX_DOC_LOCATION = "/usr/share/doc/javafx-sdk-docs-*" + +USER_DOC_LOCATION = (ENV['XDG_DATA_HOME'] || ENV['HOME'] + '/.local/share') + '/javadoc/*' + +class DocIndex + attr_reader :items + + def initialize(path) + @items = [] + find_entries(path) + end + + private + + def gen_candidates(dir) + Dir.entries(dir).reject {|e| e == '.' || + e == '..' || + e == 'class-use' || + e == 'src-html' || + (e =~ /^[a-z].*\..*/ && e != 'package-summary.html')} + end + + def find_entries(dir, path = []) + candidates = gen_candidates(dir) + candidates.each do |entry| + if entry == "package-summary.html" + @items << Dmenu::Item.new(path.join(?.), dir + ?/ + entry) + elsif entry.end_with? ".html" + @items << Dmenu::Item.new( + path.join(?.) + ?. + File.basename(entry, '.html'), + dir + ?/ + entry + ) + elsif File.directory?(dir + ?/ + entry) + find_entries(dir + ?/ + entry, path + [entry]) + end + end + end +end + + +user_libs = Dir.glob(USER_DOC_LOCATION) +java_folder = Dir.glob(JAVA_DOC_LOCATION).last +jfx_folder = Dir.glob(JAVAFX_DOC_LOCATION).last + "/html" + + +libmenu = Dmenu.new +libmenu.items = [ + Dmenu::Item.new("Java Standard Library (std)", java_folder), + Dmenu::Item.new("javafx", jfx_folder) +] + user_libs.map{|f| Dmenu::Item.new(File.basename(f), f)} + +libmenu.case_insensitive = true +libmenu.prompt = "Library:" +libmenu.lines = 20 + +menu = Dmenu.new +menu.items = DocIndex.new(libmenu.run.value).items +menu.case_insensitive = true +menu.prompt = "Javadoc:" +menu.lines = 20 +system("xdg-open #{menu.run.value}") diff --git a/bin/dmpc b/bin/dmpc new file mode 100755 index 0000000..1f33eb0 --- /dev/null +++ b/bin/dmpc @@ -0,0 +1,91 @@ +#!/bin/zsh + +dmenu_opts=("" "-i") +replace=("-r") + +zparseopts -E -D -K -help=help \ + a=artist -artist=artist \ + b=album -album=album \ + t=title -title=title \ + j=jump -jump=jump \ + r=replace -replace=replace R=replace -no-replace=replace \ + o:=dmenu_opts -dmenu-opts:=dmenu_opts \ + h:=host -host:=host \ + p:=port -port:=port + +if [ -n "$help" ]; then + <<-HELP + dmpc: manage mpd playlist with dmenu + + Usage: dmpc {-a|-b|-t|-j} [OPTION] + + Options: + -a, --artist search for artist + -b, --album search for album + -t, --title search for title + -j, --jump jump to song in current playlist (requires rofi) + (if given in addition to searches, jumps after changes) + + -r, --replace replace current playlist + -R, --no-replace do not replace current playlist + -o, --dmenu-opts additional options for dmenu + + -h, --host MPD host server + -p, --port server port + + dmpc lets you select all tracks from an artist or album or a single track. + HELP + exit 0 +fi + +if [[ -z "$artist" && -z "$album" && -z "$title" ]]; then + <<-ERR + At least one of -a, -b, -t or -j must be given. See --help for more information. + ERR + exit 1 +fi + +typeset -a queries + +mpc_() { + mpc $=host $=port "$@" +} + +dmenu_search() { + local type=$1 + mpc_ list $type "${queries[@]}" | dmenu ${=dmenu_opts[2]} +} + +add_query() { + local type=$1 + local query=$2 + + queries+=$type + queries+=$query +} + +if [[ -n $artist ]]; then + add_query albumartist "$(dmenu_search artist)" +fi + +if [[ -n $album ]]; then + add_query album "$(dmenu_search album)" +fi + +if [[ -n $title ]]; then + add_query title "$(dmenu_search title)" +fi + +if [[ ${replace[1]} == "-r" || "${replace[1]}" == "--replace" ]]; then + mpc_ clear +fi + + +if [[ ${#queries} -gt 0 ]]; then + mpc_ search "${queries[@]}" | mpc_ add + mpc_ play +fi + +if [[ -n $jump ]]; then + mpc play "$(mpc playlist | rofi -dmenu -format d)" +fi diff --git a/bin/dmscaladoc b/bin/dmscaladoc new file mode 100755 index 0000000..f59aa0f --- /dev/null +++ b/bin/dmscaladoc @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +require 'dmenu' + +JAVA_DOC_LOCATION = "/home/crater2150/manuals/scala-*" + +USER_DOC_LOCATION = (ENV['XDG_DATA_HOME'] || ENV['HOME'] + '/.local') + '/scaladoc/*' + +class DocIndex + attr_reader :items + + def initialize(path) + @items = [] + find_entries(path) + end + + private + + def gen_candidates(dir) + Dir.entries(dir).reject {|e| e == '.' || + e == '..' || + e == 'class-use' || + e == 'src-html' || + (e =~ /^[a-z].*\..*/ && e != 'package-summary.html')} + end + + def find_entries(dir, path = []) + candidates = gen_candidates(dir) + candidates.each do |entry| + if entry == "package-summary.html" + @items << Dmenu::Item.new(path.join(?.), dir + ?/ + entry) + elsif entry.end_with? ".html" + @items << Dmenu::Item.new( + path.join(?.) + ?. + File.basename(entry, '.html'), + dir + ?/ + entry + ) + elsif File.directory?(dir + ?/ + entry) + find_entries(dir + ?/ + entry, path + [entry]) + end + end + end +end + + +scala_folder = Dir.glob(JAVA_DOC_LOCATION).last + "/api/scala-library/" + +menu = Dmenu.new +menu.items = DocIndex.new(scala_folder).items +menu.case_insensitive = true +menu.prompt = "Javadoc:" +menu.lines = 20 +system("xdg-open #{menu.run.value}") diff --git a/bin/dmscrot b/bin/dmscrot new file mode 100755 index 0000000..ae0299c --- /dev/null +++ b/bin/dmscrot @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +require 'dmenu' + +def run_menu(items, prompt) + menu = Dmenu.new + menu.case_insensitive = true + menu.lines = items.length + menu.items = items + menu.prompt = prompt + menu.run.value +end +modes = [ + Dmenu::Item.new('multidisp', '-m'), + Dmenu::Item.new('select', '-s'), + Dmenu::Item.new('focused', '-u -d 1'), + Dmenu::Item.new('normal', '') +] + +actions = [ + Dmenu::Item.new('move to screenshots', 'mv $f ~/media/screenshots'), + Dmenu::Item.new('view image', 'xdg-open $f'), + Dmenu::Item.new('make draggable', 'dragon -x $f'), + Dmenu::Item.new('nothing', 'true') +] + +mode = run_menu(modes, "Screenshot type") +action = run_menu(actions, "and then") + +system('scrot', mode, '-e', action) diff --git a/bin/dmsearch b/bin/dmsearch new file mode 100755 index 0000000..646c50a --- /dev/null +++ b/bin/dmsearch @@ -0,0 +1,187 @@ +#!/bin/zsh +emulate -L zsh + +SYSTEM_CONFIG_PATH='/etc/dmsearch' +CONFIG_PATH="$HOME/.config/dmsearch" +CACHE_PATH="$HOME/.cache/dmsearch" +mkdir -p "${CACHE_PATH}" + +# required software + +depend() { + local script="$1"; shift; + local missing + local i + + for i in "$@"; do + type "$i" &>/dev/null || { + echo >&2 "$i is required for ${script}. Please install it" + missing=1 + } + done + [ -n "$missing" ] && exit 1 +} + +depend dmsearch \ + rofi \ + mv \ + tail \ + cut + + +################################################################################ +# helpers +################################################################################ + +urlencode() { + setopt extendedglob + input=$(> "$dmhistfile" + mv "$dmhistfile" "${dmhistfile}.tmp" + tail -n ${truncate} "${dmhistfile}.tmp" > "${dmhistfile}" + rm "${dmhistfile}.tmp" +} + +# freetext rofi with a default value. +# +# When called, opens a rofi with only one choice. Pressing enter without any +dmdefault() { + value=$(echo "$2" | rofi -dmenu -l 1 -p "$1" -sort-method fzf) + if [[ "$value" == "$2" ]]; then + $=3 + else + echo "$value" + fi +} + +################################################################################ +# plugin loading +################################################################################ + +# system plugins are loaded first, so they can be overridden by user plugins +for i in "(${SYSTEM_CONFIG_PATH}/searchers/"*(N) \ + "${CONFIG_PATH}/searchers/"*(N); do + . "$i" +done + +################################################################################ +# Configurable Options +################################################################################ + +# querystring + +SERCHILO="https://www.findfind.it/u/crater2150?query=" + +# some default values. all of them can be overridden by rc.zsh +typeset -A searches +searches=( g Google w Wikipedia ) +webbrowser_cmd="xdg-open" + +[[ -e "${SYSTEM_CONFIG_PATH}/rc.zsh" ]] && . "${SYSTEM_CONFIG_PATH}/rc.zsh" +[[ -e "${CONFIG_PATH}/rc.zsh" ]] && . "${CONFIG_PATH}/rc.zsh" + +################################################################################ +# main script +################################################################################ + +zparseopts -D -E -help=help h=help + +if [ -n "$help" ]; then + <<-HELP + dmsearch [-h] + Opens dmenu and lets you run web searches + + configured searches: + HELP + + for k in ${(k)searches}; do + echo " ${k}:\t${(Q)searches[$k]}"; + done + + <<-HELP + + defined module options: + HELP + for k in ${(k)searches}; do + eval "if [ \"\${#opts_$k}\" -gt 0 ]; then + echo \" Module ${(Q)searches[$k]}:\"; + for o in \${(k)opts_${k}}; do + echo \" opt_${k}_\${o}: \${opts_${k}[\$o]}\"; + done + fi" + done + exit 0 +fi + +# start dmenu +coproc dmenu -i -l 20 -p 'serchilo widget:' + +# generate menu elements +for i in ${(k)searches}; do print -p "${i} - ${searches[$i]}"; done + +# close coproc stdin +exec 5>&p 6<&p; coproc exit; exec 5>&- + +# get dmenu result +search=$(read -eu 6 | cut -d" " -f1) + +[[ -z "$search" ]] && exit 1 #user aborted + + +if which "s_${search}" &>/dev/null; then + qs=$(s_${search}) +else + qs="${SERCHILO}${search}" + result=$(dmhist "$search" | dmenu -i -l 20 -p "params to $search") + dmhistadd "$search" "$result" + qs+="+$(echo $result| urlencode)" +fi + +echo ${webbrowser_cmd} $qs +${webbrowser_cmd} $qs diff --git a/bin/dmtexdoc b/bin/dmtexdoc new file mode 100755 index 0000000..7f9851c --- /dev/null +++ b/bin/dmtexdoc @@ -0,0 +1,17 @@ +#!/bin/zsh +zparseopts -D -E -help=help h=help -update=update u=update + +CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/dtexdoc.list" +SOURCES_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/dmtexdoc/sources" + +if [[ -n "$help" ]]; then + echo "Usage: $0 [-u|--update]" + exit 1 +fi + +if [[ -n "$update" || ! -e "$CACHE_FILE" ]]; then + find -L ${$(which texdoc):h:h:h}/texmf-dist/doc/ "${(@f)$(<$SOURCES_FILE)}" \ + -iname '*.pdf' -printf "%f\n" \ + | sed -e 's/\..*//' | sort | uniq > $CACHE_FILE +fi +texdoc $(dmenu < $CACHE_FILE) diff --git a/bin/dmumount b/bin/dmumount new file mode 100755 index 0000000..bd6c1ca --- /dev/null +++ b/bin/dmumount @@ -0,0 +1,5 @@ +#!/bin/zsh +device=$(udiskie-info -a -o '{mount_path}' | grep -ve '^$' | dmenu -l 20) +if [[ -n "$device" ]]; then + udiskie-umount $device +fi diff --git a/bin/dmxrandr b/bin/dmxrandr new file mode 100755 index 0000000..6b9c6a4 --- /dev/null +++ b/bin/dmxrandr @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby + +require 'xrandr' +require 'dmenu' + +menu = Dmenu.new +menu.case_insensitive = true +menu.lines = 20 + +outputs = Xrandr::Parser.new.parse[1].group_by(&:connected) + +menu.items = (outputs[true] + outputs[false]).map{ + |o| Dmenu::Item.new(o.name + (o.connected ? ' (connected)' : ''), o) +} +menu.prompt = "output:" +output = menu.run.value + +menu.items = output.modes.map{ + |mode|Dmenu::Item.new(mode.resolution, {mode: mode.resolution}) +} +menu.items << Dmenu::Item.new("off", {off: true}) + +menu.prompt = "Current resolution: #{output.resolution}" +mode = menu.run.value + +unless mode[:off] + menu.items = [:"left-of", :"right-of", :"above", :"below"].flat_map{|dir| + outputs[true].reject{|o| o == output}.map{|out| [dir,out]} + }.map{|setting| + Dmenu::Item.new(setting[0].to_s.gsub('-',' ') + " " + setting[1].name, + {setting[0] => setting[1].name}) + } + menu.items << Dmenu::Item.new("don't change", {}) + menu.prompt = "Select position:" + mode.merge! menu.run.value +end +control = Xrandr::Control.new +control.configure(output, mode) +control.apply! diff --git a/bin/fixenc b/bin/fixenc new file mode 100755 index 0000000..5828004 --- /dev/null +++ b/bin/fixenc @@ -0,0 +1,5 @@ +#!/bin/zsh +for i in "$@"; do + dos2unix $i &>/dev/null + iconv -fLATIN1 -tUTF8 $i | sponge $i +done diff --git a/bin/idea b/bin/idea new file mode 100755 index 0000000..625d80c --- /dev/null +++ b/bin/idea @@ -0,0 +1,6 @@ +#!/bin/zsh +unset JAVA_HOME +source /etc/profile.d/11_oracle-jdk.sh +export IBUS_ENABLE_SYNC_MODE=1 +export XMODIFIERS="" +exec $(ls ${XDG_DATA_HOME:-~/.local/share}/idea-IU-*/bin/idea.sh | sort | tail -n 1) diff --git a/bin/ipy b/bin/ipy new file mode 100755 index 0000000..a2edead --- /dev/null +++ b/bin/ipy @@ -0,0 +1,20 @@ +#!/bin/zsh + +if [[ -n $VIRTUAL_ENV ]]; then + pip install ipython &>/dev/null +else + cd ~/toy-projects/playground_venv/ + . bin/activate +fi + +if [[ -e $VIRTUAL_ENV/ipython_profile ]]; then + ipy_profile="--profile=$(<$VIRTUAL_ENV/ipython_profile)" +fi + +if [[ "$1" == "pip" ]]; then + exec "$@" +elif [[ $1 == "-s" ]]; then + exec $SHELL +else + exec ipython $ipy_profile "$@" +fi diff --git a/bin/mdcat b/bin/mdcat new file mode 100755 index 0000000..a93f830 --- /dev/null +++ b/bin/mdcat @@ -0,0 +1,11 @@ +#!/bin/zsh + +MDCAT=/usr/bin/mdcat +encoding=$(file -i "$1" | sed "s/.*charset=\(.*\)$/\1/") +if ! iconv -f $encoding <<<"" &> /dev/null; then + cat "$1" +elif [[ $encoding != 'utf-8' ]]; then + iconv -f $encoding -t 'utf-8' < "$1" | $MDCAT /dev/stdin +else + $MDCAT $1 +fi diff --git a/bin/mkscript b/bin/mkscript new file mode 100755 index 0000000..da6bf21 --- /dev/null +++ b/bin/mkscript @@ -0,0 +1,13 @@ +#!/bin/zsh +BINDIR=$HOME/.local/bin +if [[ -e $BINDIR/$1 ]]; then + echo Script $1 exists, opening for edit + $EDITOR $BINDIR/$1 +else + touch $BINDIR/$1 + chmod a+x $BINDIR/$1 + echo "#!$SHELL" >> $BINDIR/$1 + $EDITOR $BINDIR/$1 + exit 0 +fi + diff --git a/bin/newmails b/bin/newmails new file mode 100755 index 0000000..8899059 --- /dev/null +++ b/bin/newmails @@ -0,0 +1,75 @@ +#!/bin/zsh + +DEFAULT=raw +ICON=/usr/share/icons/Adwaita/48x48/actions/mail-message-new.png + +opt() { + if [ -n "$1" ]; then + return 0 + else + return 1 + fi +} + +contains() { + arr=$1 + value=$2 + [[ ${${(P)arr}[(ie)$value]} -lt ${#${(P)arr}} ]] +} + +zparseopts -D -E h=help -help=help \ + r=raw -raw=raw \ + n=number -number=number \ + p=pretty -pretty=pretty + +if [ -n "$help" ]; then + cat </\>/' +} + +from() { formail -zcb -xFrom | tail -n 1 | decode } +subject() { formail -zcb -xSubject | decode } + + +if opt $number; then + echo -n $newmails | wc -l + exit 0 +fi + +if opt $pretty; then + touch $cache + seen=$(<$cache) + truncate --size 0 $cache + notify-send --icon=$ICON "$( + echo -n $newmails | while read mail; do + if ! contains seen $mail; then + echo -n "$(from < $mail): $(subject < $mail)
" + fi + echo $mail >> $cache + done + )" + exit 0 +fi + +#otherwise +echo -n $newmails diff --git a/bin/pdftosvg b/bin/pdftosvg new file mode 100755 index 0000000..5aeb7aa --- /dev/null +++ b/bin/pdftosvg @@ -0,0 +1,7 @@ +#!/bin/zsh +for i in "$@"; do + inkscape --without-gui --file=$i --export-plain-svg=/dev/stdout \ + | tr -d '\n' \ + | sed -e 's!]*>!!g' \ + | xmllint --compress /dev/stdin --output ${i:r}.svg +done diff --git a/bin/pomdep-graph b/bin/pomdep-graph new file mode 100755 index 0000000..cf5e6d9 --- /dev/null +++ b/bin/pomdep-graph @@ -0,0 +1,74 @@ +#!/bin/zsh + +zparseopts -D -K -E h=help -help=help -renderer:=renderer r:=renderer \ + f:=filter -filter:=filter \ + o:=output -output:=output \ + l:=leaf_color -leaf-color:=leaf_color \ + G=graph_only -graph-only=graph_only + +renderer=${renderer[2]:-dot} +output=${output[2]:-deps.png} +leaf_color=${leaf_color[2]:-green} + +if [[ $output == "-" ]]; then output=/dev/stdout; fi +if [[ -n $graph_only ]]; then + renderer=cat + output=/dev/stdout +fi + +if [[ -z "$2" ]]; then + echo "Usage: $0 [options] POM_FILE..." + echo + echo "Options:" + echo " -f, --filter=TEXT only show dependencies with TEXT in their group id" + echo " -r, --renderer=PROGRAM use PROGRAM for rendering. May contain additional parameters" + echo " e.g. -r \"dot -Goverlap=false\"" + echo " default: dot -Tpng" + echo " -o, --output=FILE output to FILE, defaults to deps.png" + echo " -l, --leaf-color=COLOR color of leaf nodes (packages without dependencies)" + echo " -G, --graph-only output unlayouted graph code, overrides -r and -o" + echo " equivalent to \"-r cat -o /dev/stdout\"" + +fi + +if [[ -n $filter ]]; then + DEP_PATH="//dependencies//groupId[contains(text(),'${filter[2]}')]/following-sibling::artifactId/text()" +else + DEP_PATH="//dependencies//groupId/following-sibling::artifactId/text()" +fi + +PKG_PATH="/project/artifactId/text()" + +xpath() { + xmllint --shell <(sed -e "s/xmlns=/ignore=/" $2) <<<"cat $1" | grep -v '^\(/ >\| --\)' +} + +remove-disconnected() { + gvpr -c "N[$.degree==0]{delete(NULL, $)}" "$@" +} + +color-leaf-deps() { + gvpr -c "N[$.outdegree==0]{$.color ='$1'}" +} + +graph-from-poms() { + echo 'digraph deps {' + + for pom in "$@"; do + pkg=$(xpath $PKG_PATH $pom) + deps=($(xpath $DEP_PATH $pom | grep '^[a-zA-Z]')) + + echo " \"$pkg\"" + for i in $deps; do + echo " \"$pkg\" -> \"$i\"" + done + done + + echo '}' +} + +render() { + $=renderer > $output +} + +graph-from-poms "$@" | remove-disconnected | color-leaf-deps $leaf_color | render diff --git a/bin/qrshow b/bin/qrshow new file mode 100755 index 0000000..23e2b05 --- /dev/null +++ b/bin/qrshow @@ -0,0 +1,2 @@ +#!/bin/zsh +qrencode -s 40 "$*" -o - | feh -ZF - diff --git a/bin/re b/bin/re new file mode 100755 index 0000000..093cf53 --- /dev/null +++ b/bin/re @@ -0,0 +1,40 @@ +#!/bin/zsh + +SCRIPTDIR=$XDG_CONFIG_HOME/re/ + +zparseopts -D h=help -help=help d:=dir -dir:=dir n=dry_run -dry-run=dry_run e=edit -edit=edit +if [[ -z "$@" || -n $help ]]; then + <<-HELP + re - execute regular jobs without cluttering your path + + Usage: re [opts] [scriptopts] + 1. put script in $SCRIPTDIR + 2. run "re " + 3. ... + 4. PROFIT! + + Options: + -d, --dir=DIR change to given directory + -n, --dry-run just display the script, that would be executed + -e, --edit open the script in an editor + HELP + exit +fi + +if [[ -n $dir ]]; then + cd $dir[2] +fi + +if [[ -e $SCRIPTDIR/$1 ]]; then + if [[ -n $dry_run ]]; then + $PAGER -F $SCRIPTDIR/$1 + elif [[ -n $edit ]]; then + $EDITOR $SCRIPTDIR/$1 + else + script=$1 + shift + . $SCRIPTDIR/$script "$@" + fi +else + echo "Script \"$1\" not found" +fi diff --git a/bin/tmux-url-fuzz b/bin/tmux-url-fuzz new file mode 100755 index 0000000..7432dc9 --- /dev/null +++ b/bin/tmux-url-fuzz @@ -0,0 +1,5 @@ +#!/bin/zsh +target=$(tmux capture-pane -e -p -J -S -20 \ + | grep -oP "(https?://|www\.)[^\"<>') \e]*" \ + | ifne fzf-tmux --tac) +if [[ -n $target ]]; then xdg-open $target; fi diff --git a/bin/unlock-parse b/bin/unlock-parse new file mode 100755 index 0000000..8655bfa --- /dev/null +++ b/bin/unlock-parse @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +require 'time' + +def valid_log(lines) + lines.reduce(["L",true]){|acc,line| + if acc[1] && line[0] != acc[0] then + [line[0], true] + else + [nil, false] + end + }[1] +end + +if ARGV[0] == '-h' then + puts <<~HELP + Usage: unlock-parse [-s] DAY... + -s short output, total only + HELP + exit +end + +if ARGV[0] == '-s' then + short=true + days=ARGV[1..-1] +else + short=false + days=ARGV +end + +days.empty? && days = [Date.today.iso8601] + +days.each do |logday| + + + path = if File.exists? logday then logday else ENV['XDG_DATA_HOME'] + '/log/locktime/' + logday end + + log = File.readlines(path) + + if ! valid_log(log) then puts "Invalid log file"; exit 1 end + + if log.length % 2 != 0 then + log << "L " + Time.now.iso8601 + end + + times = log.each_slice(2).map{|e| + Time.parse(e[1][2..-1]) - Time.parse(e[0][2..-1]) + } + + if ! short then + times.map{|t| Time.at(t).utc.strftime("%H:%M:%S")}.each{|s|puts s} + puts "\nTotal:" + end + puts Time.at(times.sum).utc.strftime("%H:%M:%S") + +end diff --git a/bin/unlock-track b/bin/unlock-track new file mode 100755 index 0000000..b0385d6 --- /dev/null +++ b/bin/unlock-track @@ -0,0 +1,22 @@ +#!/bin/zsh + +lockstate() { pidof slimlock &>/dev/null } + +lockstate; was_locked=$? + +LOGDIR=${XDG_DATA_HOME:-$HOME/.local/share}/log/locktime +mkdir -p $LOGDIR +logfile=$LOGDIR/$(date --iso-8601) + +while true; do + if lockstate && [[ $was_locked -gt 0 ]]; then + echo "L $(date --iso-8601=minute)" >> $logfile + was_locked=0 + elif ! lockstate && [[ $was_locked == 0 ]]; then + # change daily logfile on first unlock + logfile=$LOGDIR/$(date --iso-8601) + echo "U $(date --iso-8601=minute)" >> $logfile + was_locked=1 + fi + sleep 60 +done diff --git a/bin/update-amm b/bin/update-amm new file mode 100755 index 0000000..5877aa7 --- /dev/null +++ b/bin/update-amm @@ -0,0 +1,13 @@ +#!/bin/zsh +versions=$(curl -L https://api.github.com/repos/lihaoyi/Ammonite/releases/latest) + +download-version() { + ( + echo "#/usr/bin/env python --version sh" && \ + curl -L $( echo $versions \ + | jq -r ".assets | map(select(.name | startswith(\"$1\")))[0].browser_download_url") + ) > $2 + chmod +x $2 +} +download-version 2.13 $(which amm) +download-version 2.12 $(which amm)2.12 diff --git a/bin/venv b/bin/venv new file mode 100755 index 0000000..c760019 --- /dev/null +++ b/bin/venv @@ -0,0 +1,36 @@ +#!/bin/zsh + +zparseopts -D -E c=cdhere -cdhere=cdhere h=help -help=help +if [[ -n $help ]]; then + <<-HELP + Usage: ${0:t} [-c] [DIR] + + If DIR contains bin/activate, start a shell with that virtualenv. + Otherwise recursively look up virtualenvs in DIR (by looking for dirs ending + in "venv") and select one with fzf. + + -c, --cdhere Change back to the current working directory instead of the + virtual env's base dir + HELP + exit +fi + +ORIGDIR=$PWD + +venvsh() { + cd $1 + . bin/activate + if [[ -n $cdhere ]]; then + cd $ORIGDIR + fi + $SHELL +} + +if [[ -n $1 && -e $1/bin/activate ]]; then + exec venvsh $1 +fi + +venv=$(fd -I -t d 'venv$' $1 | fzf) +if [[ -n $venv ]]; then + venvsh $venv +fi diff --git a/bin/vf b/bin/vf new file mode 100755 index 0000000..b33d9ee --- /dev/null +++ b/bin/vf @@ -0,0 +1,20 @@ +#!/bin/zsh +zparseopts -D -E -ext:=ext e:=ext h=help -help=help + +if [[ -n $help ]]; then + <<-HELP + Usage: ${0:t} [-e EXT | PATTERN] [DIR...] + + Find files with fd, show them with fzf and open selected files via xdg-open. + + Either -e with a file extension or a pattern can be given to narrow search. + Any further arguments are passed to fd as search path. + HELP + exit +fi + +if [[ -n $ext ]]; then + fd -e ${ext[2]} . "$@" +else + fd "$@" +fi | fzf | xargs --null xdg-open diff --git a/bin/webapp b/bin/webapp new file mode 100755 index 0000000..39a7625 --- /dev/null +++ b/bin/webapp @@ -0,0 +1,10 @@ +#!/bin/zsh +profile="${0:t}" + +need firefox +need firejail + +mkdir -p $HOME/.jails/firefox-$profile + +exec firejail --private=$HOME/.jails/firefox-$profile \ + firefox --no-remote "$@" diff --git a/bin/webapp-cp b/bin/webapp-cp new file mode 100755 index 0000000..0a589ef --- /dev/null +++ b/bin/webapp-cp @@ -0,0 +1,3 @@ +#!/bin/zsh +profile="${${0:t}%%-cp}" +cp "$@" $HOME/.jails/firefox-$profile diff --git a/bin/xc b/bin/xc new file mode 100755 index 0000000..8565a42 --- /dev/null +++ b/bin/xc @@ -0,0 +1,83 @@ +#!/bin/zsh + +# install this program with the name xc and symlink it as xs. +# xc will use the clipboard selection, while xs while use the primary selection + +source $(which need) +need xclip + +function get_primary() { xclip -o -selection primary } +function get_clipboard() { xclip -o -selection clipboard } +function set_primary() { xclip -i -selection primary } +function set_clipboard() { xclip -i -selection clipboard } +function show_both() { + printf "\e[1;94mPrimary\e[0m\n" + get_primary + echo + printf "\e[1;94mClipboard\e[0m\n" + get_clipboard +} + +function read_both() { + need pee moreutils + pee "xclip -i -selection primary" "xclip -i -selection clipboard" +} + +function common() { + cmd=$1 + getc=$2 + setc=$3 + shift 3 + case "$cmd" in + "read") $setc;; + "readboth") read_both ;; + "showboth") show_both ;; + "sed") $getc | sed -e "$@" | $setc; $getc;; + "pipe") $getc | "$@" | $setc; $getc;; + "edit") need vipe moreutils; $getc | vipe | $setc; $getc;; + "") $getc ;; + *) <<-HELP + Usage: xc [COMMAND] + xc fromxs|toxs + xs [COMMAND] + xs fromxc|toxc + + The used X selection is determined by the name the program is called as: + xc: clipboard (Ctrl-C / Ctrl-V) + xs: primary selection (middle mouse button) + For showboth/readboth, the action is done for both selections. + + Commands: + show/showboth: show contents of clipboard (default if no command given) + read/readboth: store input to clipboard + sed: modify contents using a sed expression + pipe: modify contents by piping through a command + edit: edit clipboard contents using \$EDITOR + + fromxs/toxs: xc only: copy contents from or to primary selection + fromxc/toxc: xs only: copy contents from or to clipboard + HELP + ;; + esac +} + +cmd=$1 +[[ -n $1 ]] && shift + +case "${0:t}" in + xc) + case "$cmd" in + "fromxs"|"fromXS") get_primary | set_clipboard ;; + "toxs"|"toXS") get_clipboard | set_primary ;; + *) common "$cmd" get_clipboard set_clipboard "$@";; + esac + ;; + xs) + case "$cmd" in + "fromxc"|"fromXC") get_clipboard | set_primary ;; + "toxc"|"toXC") get_primary | set_clipboard ;; + *) common "$cmd" get_primary set_primary "$@";; + esac + ;; +esac +