minor updates and addition of personal scripts to local/bin

This commit is contained in:
Solomon Laing 2023-02-11 15:44:26 +10:30
parent 9ff875b84f
commit 5417494fc3
78 changed files with 4453 additions and 2 deletions

View File

@ -7,7 +7,7 @@ case $- in
esac
export GOPATH="$HOME/go"
export PATH="$PATH:$HOME/.local/bin:$GOPATH/bin"
export PATH="$PATH:$HOME/.local/bin:$GOPATH/bin:$HOME/scripts"
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options

View File

@ -178,6 +178,8 @@ map gcxm cd ~/.xmonad
map gcs cd ~/.config/shell
map gcz cd ~/.config/zsh
map gcx1 cd ~/.config/x11
map gll cd ~/.local
map glb cd ~/.local/bin
map gw cd ~/work
map gNN cd ~/notes/
map gNw cd ~/notes/zk/work/

View File

@ -1,6 +1,6 @@
# If you come from bash you might have to change your $PATH.
export GOPATH="$HOME/go"
export PATH=$HOME/.local/bin:$GOPATH/bin:$PATH
export PATH=$HOME/.local/bin:$GOPATH/bin:$PATH:$HOME/scripts
# Path to your oh-my-zsh installation.
export ZSH="/home/solomon/.oh-my-zsh"

12
.local/bin/bash-status-bat Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
[[ "$(upower -i $(upower -e | grep 'BAT'))" == "" ]] && exit
current=$(upower -i $(upower -e | grep 'BAT') | grep -E "percentage" | sed 's/.*://' | sed 's/ *//')
state=$(upower -i $(upower -e | grep 'BAT') | grep -E "state" | sed 's/.*://' | sed 's/ *//')
if [[ "$state" == "not charging" ]]; then
state="full"
fi
echo "$current $state"

22
.local/bin/bash-status-git Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
if git status &>/dev/null; then
branch=$(git branch --show-current)
if [ -z "$branch" ]; then
echo "(detached)"
exit 0
fi
read -ra counts <<< "$(git rev-list --left-right --count "$branch"...origin/"$branch")"
to_push=${counts[0]}
to_pull=${counts[1]}
extras=$([[ ${counts[0]} -eq 0 && ${counts[1]} -eq 0 ]] || echo " $to_push↑ $to_pull↓")
if [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]]; then
echo "($branch*$extras)"
else
echo "($branch~$extras)"
fi
fi

70
.local/bin/bashmatrix Executable file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Courtesy of @bvierra and company (long ago, pre-cPanel)
### Customization:
blue="\033[0;34m"
brightblue="\033[1;34m"
cyan="\033[0;36m"
brightcyan="\033[1;36m"
green="\033[0;32m"
brightgreen="\033[1;32m"
red="\033[0;31m"
brightred="\033[1;31m"
white="\033[1;37m"
black="\033[0;30m"
grey="\033[0;37m"
darkgrey="\033[1;30m"
# Choose the colors that will be used from the above list
# space-separated list
# e.g. `colors=($green $brightgreen $darkgrey $white)`
colors=($green $brightgreen)
### End customization
### Do not edit below this line
spacing=${1:-100} # the likelihood of a character being left in place
scroll=${2:-0} # 0 for static, positive integer determines scroll speed
screenlines=$(expr `tput lines` - 1 + $scroll)
screencols=$(expr `tput cols` / 2 - 1)
# chars=(a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ^)
# charset via Carl:
chars=(ア イ ウ エ オ カ キ ク ケ コ サ シ ス セ ソ タ チ ツ テ ト ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ マ ミ ム メ モ ヤ ユ ヨ ラ リ ル レ ロ ワ ン)
count=${#chars[@]}
colorcount=${#colors[@]}
trap "tput sgr0; clear; exit" SIGTERM SIGINT
if [[ $1 =~ '-h' ]]; then
echo "Display a Matrix(ish) screen in the terminal"
echo "Usage: matrix [SPACING [SCROLL]]"
echo "Example: matrix 100 0"
exit 0
fi
clear
tput cup 0 0
while :
do for i in $(eval echo {1..$screenlines})
do for i in $(eval echo {1..$screencols})
do rand=$(($RANDOM%$spacing))
case $rand in
0)
printf "${colors[$RANDOM%$colorcount]}${chars[$RANDOM%$count]} "
;;
1)
printf " "
;;
*)
printf "\033[2C"
;;
esac
done
printf "\n"
# sleep .005
done
tput cup 0 0
done

29
.local/bin/bw-unlock Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
##
# Depends: zenity, bw (bitwarden-cli), notify-send
#
# Generates bw session key and saves it in tmp file where only current user has permissions
#
# By Solomon Laing (solomonlaing@pm.me)
# Date 2020-03-26
##
password=$(zenity --password)
if [ -z $password ]
then
exit 0;
fi
session=$(bw unlock "$password" | awk 'NF{last=$NF} END{print last}')
loc="/tmp/bw-session"
touch $loc -f
chmod 600 $loc
echo "$session" > /tmp/bw-session
notify-send "Your bitwarden vault has been unlocked until next reboot."

17
.local/bin/checkup Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
# Syncs repositories and downloads updates, meant to be run as a cronjob.
notify-send "📦 Repository Sync" "Checking for package updates..."
sudo pacman -Syyuw --noconfirm || notify-send "Error downloading updates.
Check your internet connection, if pacman is already running, or run update manually to see errors."
pkill -RTMIN+8 "${STATUSBAR:-dwmblocks}"
if pacman -Qu | grep -v "\[ignored\]"
then
notify-send "🎁 Repository Sync" "Updates available. Click statusbar icon (📦) for update."
else
notify-send "📦 Repository Sync" "Sync complete. No new packages for update."
fi

19
.local/bin/cht.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
commands="rsync find man tldr sed awk tr cp ls grep xargs rg ps mv kill lsof less head tail tar cp rm rename jq cat ssh cargo git git-worktree git-status git-commit git-rebase docker docker-compose stow chmod chown make"
languages="golang solidity vlang v nodejs javascript tmux typescript zsh cpp c lua rust python bash php haskell ArnoldC css html gdb"
selected=$(echo "$commands $languages" | tr ' ' '\n' | fzf)
if [[ -z $selected ]]; then
exit 0
fi
read -p "Enter Query: " query
if grep -qs "$selected" "$languages"; then
query=$(echo "$query" | tr ' ' '+')
tmux neww bash -c "curl cht.sh/$selected/$query & while [ : ]; do sleep 1; done"
else
tmux neww bash -c "curl -s cht.sh/$selected~$query | less"
fi

59
.local/bin/compiler Executable file
View File

@ -0,0 +1,59 @@
#!/bin/sh
# This script will compile or run another finishing operation on a document. I
# have this script run via vim.
#
# Compiles .tex. groff (.mom, .ms), .rmd, .md, .org. Opens .sent files as sent
# presentations. Runs scripts based on extension or shebang.
#
# Note that .tex files which you wish to compile with XeLaTeX should have the
# string "xelatex" somewhere in a comment/command in the first 5 lines.
file=$(readlink -f "$1")
dir=${file%/*}
base="${file%.*}"
ext="${file##*.}"
cd "$dir" || exit 1
textype() { \
textarget="$(getcomproot "$file" || echo "$file")"
echo "$textarget"
command="pdflatex"
( head -n5 "$textarget" | grep -qi 'xelatex' ) && command="xelatex"
$command --output-directory="${textarget%/*}" "${textarget%.*}"
grep -qi addbibresource "$textarget" &&
biber --input-directory "${textarget%/*}" "${textarget%.*}" &&
$command --output-directory="${textarget%/*}" "${textarget%.*}" &&
$command --output-directory="${textarget%/*}" "${textarget%.*}"
}
case "$ext" in
# Try to keep these cases in alphabetical order.
[0-9]) preconv "$file" | refer -PS -e | groff -mandoc -T pdf > "$base".pdf ;;
c) cc "$file" -o "$base" && "$base" ;;
cpp) g++ "$file" -o "$base" && "$base" ;;
cs) mcs "$file" && mono "$base".exe ;;
go) go run "$file" ;;
h) sudo make install ;;
java) javac -d classes "$file" && java -cp classes "${1%.*}" ;;
m) octave "$file" ;;
md) if [ -x "$(command -v lowdown)" ]; then
lowdown --parse-no-intraemph "$file" -Tms | groff -mpdfmark -ms -kept -T pdf > "$base".pdf
elif [ -x "$(command -v groffdown)" ]; then
groffdown -i "$file" | groff -T pdf > "$base".pdf
else
pandoc -t ms --highlight-style=kate -s -o "$base".pdf "$file"
fi ; ;;
mom) preconv "$file" | refer -PS -e | groff -mom -kept -T pdf > "$base".pdf ;;
ms) preconv "$file" | refer -PS -e | groff -me -ms -kept -T pdf > "$base".pdf ;;
org) emacs "$file" --batch -u "$USER" -f org-latex-export-to-pdf ;;
py) python "$file" ;;
[rR]md) Rscript -e "rmarkdown::render('$file', quiet=TRUE)" ;;
rs) cargo build ;;
sass) sassc -a "$file" "$base".css ;;
scad) openscad -o "$base".stl "$file" ;;
sent) setsid -f sent "$file" 2>/dev/null ;;
tex) textype "$file" ;;
*) sed -n '/^#!/s/^#!//p; q' "$file" | xargs -r -I % "$file" ;;
esac

11
.local/bin/cron/README.md Normal file
View File

@ -0,0 +1,11 @@
# Important Note
These cronjobs have components that require information about your current display to display notifications correctly.
When you add them as cronjobs, I recommend you precede the command with commands as those below:
```
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $USER)/bus; export DISPLAY=:0; . $HOME/.zprofile; then_command_goes_here
```
This ensures that notifications will display, xdotool commands will function and environmental variables will work as well.

17
.local/bin/cron/checkup Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
# Syncs repositories and downloads updates, meant to be run as a cronjob.
notify-send "📦 Repository Sync" "Checking for package updates..."
sudo pacman -Syyuw --noconfirm || notify-send "Error downloading updates.
Check your internet connection, if pacman is already running, or run update manually to see errors."
pkill -RTMIN+8 "${STATUSBAR:-dwmblocks}"
if pacman -Qu | grep -v "\[ignored\]"
then
notify-send "🎁 Repository Sync" "Updates available. Click statusbar icon (📦) for update."
else
notify-send "📦 Repository Sync" "Sync complete. No new packages for update."
fi

6
.local/bin/cron/crontog Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
# Toggles all cronjobs off/on.
# Stores disabled crontabs in ~/.config/cronsaved until restored.
([ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved ] && crontab - < "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && rm "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && notify-send "🕓 Cronjobs re-enabled.") || ( crontab -l > "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && crontab -r && notify-send "🕓 Cronjobs saved and disabled.")

6
.local/bin/crontog Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
# Toggles all cronjobs off/on.
# Stores disabled crontabs in ~/.config/cronsaved until restored.
([ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved ] && crontab - < "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && rm "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && notify-send "🕓 Cronjobs re-enabled.") || ( crontab -l > "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && crontab -r && notify-send "🕓 Cronjobs saved and disabled.")

11
.local/bin/dec-sink-volume Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
default_loc=/tmp/pactl-default-sink
if [ ! -f "$default_loc" ] ; then
set-default-sink
fi
default=$(cat $default_loc)
pactl set-sink-volume $default -5%

11
.local/bin/dec-source-volume Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
default_loc=/tmp/pactl-default-source
if [ ! -f "$default_loc" ] ; then
set-default-source
fi
default=$(cat $default_loc)
pactl set-source-volume $default -5%

83
.local/bin/displayselect Executable file
View File

@ -0,0 +1,83 @@
#!/bin/sh
# A UI for detecting and selecting all displays. Probes xrandr for connected
# displays and lets user select one to use. User may also select "manual
# selection" which opens arandr.
twoscreen() { # If multi-monitor is selected and there are two screens.
mirror=$(printf "no\\nyes" | dmenu -i -p "Mirror displays?")
# Mirror displays using native resolution of external display and a scaled
# version for the internal display
if [ "$mirror" = "yes" ]; then
external=$(echo "$screens" | dmenu -i -p "Optimize resolution for:")
internal=$(echo "$screens" | grep -v "$external")
res_external=$(xrandr --query | sed -n "/^$external/,/\+/p" | \
tail -n 1 | awk '{print $1}')
res_internal=$(xrandr --query | sed -n "/^$internal/,/\+/p" | \
tail -n 1 | awk '{print $1}')
res_ext_x=$(echo "$res_external" | sed 's/x.*//')
res_ext_y=$(echo "$res_external" | sed 's/.*x//')
res_int_x=$(echo "$res_internal" | sed 's/x.*//')
res_int_y=$(echo "$res_internal" | sed 's/.*x//')
scale_x=$(echo "$res_ext_x / $res_int_x" | bc -l)
scale_y=$(echo "$res_ext_y / $res_int_y" | bc -l)
xrandr --output "$external" --auto --scale 1.0x1.0 \
--output "$internal" --auto --same-as "$external" \
--scale "$scale_x"x"$scale_y"
else
primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
secondary=$(echo "$screens" | grep -v "$primary")
direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
xrandr --output "$primary" --auto --scale 1.0x1.0 --output "$secondary" --"$direction"-of "$primary" --auto --scale 1.0x1.0
fi
}
morescreen() { # If multi-monitor is selected and there are more than two screens.
primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
secondary=$(echo "$screens" | grep -v "$primary" | dmenu -i -p "Select secondary display:")
direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
tertiary=$(echo "$screens" | grep -v "$primary" | grep -v "$secondary" | dmenu -i -p "Select third display:")
xrandr --output "$primary" --auto --output "$secondary" --"$direction"-of "$primary" --auto --output "$tertiary" --"$(printf "left\\nright" | grep -v "$direction")"-of "$primary" --auto
}
multimon() { # Multi-monitor handler.
case "$(echo "$screens" | wc -l)" in
2) twoscreen ;;
*) morescreen ;;
esac ;}
onescreen() { # If only one output available or chosen.
xrandr --output "$1" --auto --scale 1.0x1.0 $(echo "$allposs" | grep -v "\b$1" | awk '{print "--output", $1, "--off"}' | paste -sd ' ' -)
}
postrun() { # Stuff to run to clean up.
setbg # Fix background if screen size/arangement has changed.
remaps # Re-remap keys if keyboard added (for laptop bases)
{ killall dunst ; setsid -f dunst ;} >/dev/null 2>&1 # Restart dunst to ensure proper location on screen
}
# Get all possible displays
allposs=$(xrandr -q | grep "connected")
# Get all connected screens.
screens=$(echo "$allposs" | awk '/ connected/ {print $1}')
# If there's only one screen
[ "$(echo "$screens" | wc -l)" -lt 2 ] &&
{ onescreen "$screens"; postrun; notify-send "💻 Only one screen detected." "Using it in its optimal settings..."; exit ;}
# Get user choice including multi-monitor and manual selection:
chosen=$(printf "%s\\nmulti-monitor\\nmanual selection" "$screens" | dmenu -i -p "Select display arangement:") &&
case "$chosen" in
"manual selection") arandr ; exit ;;
"multi-monitor") multimon ;;
*) onescreen "$chosen" ;;
esac
postrun

317
.local/bin/dmenu-bluetooth Executable file
View File

@ -0,0 +1,317 @@
#!/usr/bin/env bash
# _ _ _ _ _ _
# __| |_ __ ___ ___ _ __ _ _ | |__ | |_ _ ___| |_ ___ ___ | |_ | |__
# / _` | '_ ` _ \ / _ \ '_ \| | | |_____| '_ \| | | | |/ _ \ __/ _ \ / _ \| __|| '_ \
# | (_| | | | | | | __/ | | | |_| |_____| |_) | | |_| | __/ || (_) | (_) | |_ | | | |
# \__,_|_| |_| |_|\___|_| |_|\__,_| |_.__/|_|\__,_|\___|\__\___/ \___/ \__||_| |_|
#
# Author: Nick Clyde (clydedroid)
# dmenu support by: Layerex
#
# A script that generates a dmenu menu that uses bluetoothctl to
# connect to bluetooth devices and display status info.
#
# Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu)
# Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl)
#
# Depends on:
# Arch repositories: dmenu, bluez-utils (contains bluetoothctl)
# Constants
divider="---------"
goback="Back"
# Checks if bluetooth controller is powered on
power_on() {
if bluetoothctl show | grep -F -q "Powered: yes"; then
return 0
else
return 1
fi
}
# Toggles power state
toggle_power() {
if power_on; then
bluetoothctl power off
show_menu
else
if rfkill list bluetooth | grep -F -q 'blocked: yes'; then
rfkill unblock bluetooth && sleep 3
fi
bluetoothctl power on
show_menu
fi
}
# Checks if controller is scanning for new devices
scan_on() {
if bluetoothctl show | grep -F -q "Discovering: yes"; then
echo "Scan: on"
return 0
else
echo "Scan: off"
return 1
fi
}
# Toggles scanning state
toggle_scan() {
if scan_on; then
kill "$(pgrep -F -f "bluetoothctl scan on")"
bluetoothctl scan off
show_menu
else
bluetoothctl scan on &
echo "Scanning..."
sleep 5
show_menu
fi
}
# Checks if controller is able to pair to devices
pairable_on() {
if bluetoothctl show | grep -F -q "Pairable: yes"; then
echo "Pairable: on"
return 0
else
echo "Pairable: off"
return 1
fi
}
# Toggles pairable state
toggle_pairable() {
if pairable_on; then
bluetoothctl pairable off
show_menu
else
bluetoothctl pairable on
show_menu
fi
}
# Checks if controller is discoverable by other devices
discoverable_on() {
if bluetoothctl show | grep -F -q "Discoverable: yes"; then
echo "Discoverable: on"
return 0
else
echo "Discoverable: off"
return 1
fi
}
# Toggles discoverable state
toggle_discoverable() {
if discoverable_on; then
bluetoothctl discoverable off
show_menu
else
bluetoothctl discoverable on
show_menu
fi
}
# Checks if a device is connected
device_connected() {
device_info=$(bluetoothctl info "$1")
if echo "$device_info" | grep -F -q "Connected: yes"; then
return 0
else
return 1
fi
}
# Toggles device connection
toggle_connection() {
if device_connected "$1"; then
bluetoothctl disconnect "$1"
# device_menu "$device"
else
bluetoothctl connect "$1"
# device_menu "$device"
fi
}
# Checks if a device is paired
device_paired() {
device_info=$(bluetoothctl info "$1")
if echo "$device_info" | grep -F -q "Paired: yes"; then
echo "Paired: yes"
return 0
else
echo "Paired: no"
return 1
fi
}
# Toggles device paired state
toggle_paired() {
if device_paired "$1"; then
bluetoothctl remove "$1"
device_menu "$device"
else
bluetoothctl pair "$1"
device_menu "$device"
fi
}
# Checks if a device is trusted
device_trusted() {
device_info=$(bluetoothctl info "$1")
if echo "$device_info" | grep -F -q "Trusted: yes"; then
echo "Trusted: yes"
return 0
else
echo "Trusted: no"
return 1
fi
}
# Toggles device connection
toggle_trust() {
if device_trusted "$1"; then
bluetoothctl untrust "$1"
device_menu "$device"
else
bluetoothctl trust "$1"
device_menu "$device"
fi
}
# Prints a short string with the current bluetooth status
# Useful for status bars like polybar, etc.
print_status() {
if power_on; then
printf ''
mapfile -t paired_devices < <(bluetoothctl paired-devices | grep -F Device | cut -d ' ' -f 2)
counter=0
for device in "${paired_devices[@]}"; do
if device_connected "$device"; then
device_alias="$(bluetoothctl info "$device" | grep -F "Alias" | cut -d ' ' -f 2-)"
if [ $counter -gt 0 ]; then
printf ", %s" "$device_alias"
else
printf " %s" "$device_alias"
fi
((counter++))
fi
done
printf "\n"
else
echo ""
fi
}
# A submenu for a specific device that allows connecting, pairing, and trusting
device_menu() {
device=$1
# Get device name and mac address
device_name="$(echo "$device" | cut -d ' ' -f 3-)"
mac="$(echo "$device" | cut -d ' ' -f 2)"
# Build options
if device_connected "$mac"; then
connected="Connected: yes"
else
connected="Connected: no"
fi
paired=$(device_paired "$mac")
trusted=$(device_trusted "$mac")
options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit"
# Open dmenu menu, read chosen option
chosen="$(echo -e "$options" | run_dmenu "$device_name")"
# Match chosen option to command
case $chosen in
"" | "$divider")
echo "No option chosen."
;;
"$connected")
toggle_connection "$mac"
;;
"$paired")
toggle_paired "$mac"
;;
"$trusted")
toggle_trust "$mac"
;;
"$goback")
show_menu
;;
esac
}
# Opens a dmenu menu with current bluetooth status and options to connect
show_menu() {
# Get menu options
if power_on; then
power="Power: on"
# Human-readable names of devices, one per line
# If scan is off, will only list paired devices
devices=$(bluetoothctl devices | grep -F Device | cut -d ' ' -f 3-)
# Get controller flags
scan=$(scan_on)
pairable=$(pairable_on)
discoverable=$(discoverable_on)
# Options passed to dmenu
options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit"
else
power="Power: off"
options="$power\nExit"
fi
# Open dmenu menu, read chosen option
chosen="$(echo -e "$options" | run_dmenu "Bluetooth")"
# Match chosen option to command
case $chosen in
"" | "$divider")
echo "No option chosen."
;;
"$power")
toggle_power
;;
"$scan")
toggle_scan
;;
"$discoverable")
toggle_discoverable
;;
"$pairable")
toggle_pairable
;;
*)
device=$(bluetoothctl devices | grep -F "$chosen")
# Open a submenu if a device is selected
if [[ $device ]]; then device_menu "$device"; fi
;;
esac
}
original_args=("$@")
# dmenu command to pipe into. Extra arguments to dmenu-bluetooth are passed through to dmenu. This
# allows the user to set fonts, sizes, colours, etc.
run_dmenu() {
dmenu "${original_args[@]}" -i -p "$1"
}
case "$1" in
--status)
print_status
;;
*)
show_menu
;;
esac

67
.local/bin/dmenumount Executable file
View File

@ -0,0 +1,67 @@
#!/bin/sh
# Gives a dmenu prompt to mount unmounted drives and Android phones. If
# they're in /etc/fstab, they'll be mounted automatically. Otherwise, you'll
# be prompted to give a mountpoint from already existsing directories. If you
# input a novel directory, it will prompt you to create that directory.
getmount() { \
[ -z "$chosen" ] && exit 1
# shellcheck disable=SC2086
mp="$(find $1 2>/dev/null | dmenu -i -p "Type in mount point.")" || exit 1
test -z "$mp" && exit 1
if [ ! -d "$mp" ]; then
mkdiryn=$(printf "No\\nYes" | dmenu -i -p "$mp does not exist. Create it?") || exit 1
[ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || sudo -A mkdir -p "$mp")
fi
}
mountusb() { \
chosen="$(echo "$usbdrives" | dmenu -i -p "Mount which drive?")" || exit 1
chosen="$(echo "$chosen" | awk '{print $1}')"
sudo -A mount "$chosen" 2>/dev/null && notify-send "💻 USB mounting" "$chosen mounted." && exit 0
alreadymounted=$(lsblk -nrpo "name,type,mountpoint" | awk '$3!~/\/boot|\/home$|SWAP/&&length($3)>1{printf "-not ( -path *%s -prune ) ",$3}')
getmount "/mnt /media /mount /home -maxdepth 5 -type d $alreadymounted"
partitiontype="$(lsblk -no "fstype" "$chosen")"
case "$partitiontype" in
"vfat") sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000;;
"exfat") sudo -A mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)";;
*) sudo -A mount "$chosen" "$mp"; user="$(whoami)"; ug="$(groups | awk '{print $1}')"; sudo -A chown "$user":"$ug" "$mp";;
esac && notify-send "💻 USB mounting" "$chosen mounted to $mp." ||
notify-send "💻 Drive failed to mount." "Probably a permissions issue or drive is already mounted."
}
mountandroid() { \
chosen="$(echo "$anddrives" | dmenu -i -p "Which Android device?")" || exit 1
chosen="$(echo "$chosen" | cut -d : -f 1)"
getmount "$HOME -maxdepth 3 -type d"
echo "OK" | dmenu -i -p "Tap Allow on your phone if it asks for permission and then press enter" || exit 1
simple-mtpfs --device "$chosen" "$mp" &&
notify-send "🤖 Android Mounting" "Android device mounted to $mp." ||
notify-send "🤖 Android failed mounting." "Probably a permissions issue or phone is already mounted."
}
asktype() { \
choice="$(printf "USB\\nAndroid" | dmenu -i -p "Mount a USB drive or Android device?")" || exit 1
case $choice in
USB) mountusb ;;
Android) mountandroid ;;
esac
}
anddrives=$(simple-mtpfs -l 2>/dev/null)
usbdrives="$(lsblk -rpo "name,type,size,label,mountpoint,fstype" | grep -v crypto_LUKS | grep 'part\|rom' | sed 's/ /:/g' | awk -F':' '$5==""{printf "%s (%s) %s\n",$1,$3,$4}')"
if [ -z "$usbdrives" ]; then
[ -z "$anddrives" ] && echo "No USB drive or Android device detected" && exit
echo "Android device(s) detected."
mountandroid
else
if [ -z "$anddrives" ]; then
echo "USB drive(s) detected."
mountusb
else
echo "Mountable USB drive(s) and Android device(s) detected."
asktype
fi
fi

19
.local/bin/dmenumountcifs Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
# Gives a dmenu prompt to mount unmounted local NAS shares for read/write.
# Requirements - "%wheel ALL=(ALL) NOPASSWD: ALL"
#
# Browse for mDNS/DNS-SD services using the Avahi daemon...
srvname=$(avahi-browse _smb._tcp -t | awk '{print $4}' | dmenu -i -p "Which NAS?") || exit 1
notify-send "Searching for network shares..." "Please wait..."
# Choose share disk...
share=$(smbclient -L "$srvname" -N | grep Disk | awk '{print $1}' | dmenu -i -p "Mount which share?") || exit 1
# Format URL...
share2mnt=//"$srvname".local/"$share"
sharemount() {
mounted=$(mount -v | grep "$share2mnt") || ([ ! -d /mnt/"$share" ] && sudo mkdir /mnt/"$share")
[ -z "$mounted" ] && sudo mount -t cifs "$share2mnt" -o user=nobody,password="",noperm /mnt/"$share" && notify-send "Netshare $share mounted" && exit 0
notify-send "Netshare $share already mounted"; exit 1
}
sharemount

6
.local/bin/dmenupass Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
# This script is the SUDO_ASKPASS variable, meaning that it will be used as a
# password prompt if needed.
dmenu -fn Monospace-18 -P -p "$1" <&- && echo

21
.local/bin/dmenuprompt Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
# A dmenu binary prompt script.
# Gives a dmenu prompt labelled with $1 to perform command $2.
# To invert the options, simply provide a thrid parameter $3.
# For example:
# `./prompt "Do you want to shutdown?" "shutdown now"`
# Taken shamelessly from Luke Smith
if [ -n "$3" ]
then
choice=$(echo -e "Yes\nNo" | dmenu -bw 0 -i -p "$1")
else
choice=$(echo -e "No\nYes" | dmenu -bw 0 -i -p "$1")
fi
if [ $choice = "Yes" ]
then
$2
fi

91
.local/bin/dmenusearch Executable file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env bash
#
# Script name: dmenusearch
# Description: Search various search engines (inspired by surfraw).
# Dependencies: dmenu and a web browser
# Usage: dmenusearch <engine>
# where engine is amazon, duckduckgo, etc.
# without engine all options will be listed
# Pilfered from Derek Taylor @ https://www.gitlab.com/dwt1/dmscripts
# Contributors: Derek Taylor
# Ali Furkan Yıldız
# Defining our web browser.
DMBROWSER="${BROWSER:-surf}"
# An array of search engines. You can edit this list to add/remove
# search engines. The format must be: "engine_name]="url".
# The url format must allow for the search keywords at the end of the url.
# For example: https://www.amazon.com/s?k=XXXX searches Amazon for 'XXXX'.
declare -A options
options[amazon]="https://www.amazon.com/s?k="
options[archaur]="https://aur.archlinux.org/packages/?O=0&K="
options[archpkg]="https://archlinux.org/packages/?sort=&q="
options[archwiki]="https://wiki.archlinux.org/index.php?search="
options[arxiv]="https://arxiv.org/search/?searchtype=all&source=header&query="
options[bbcnews]="https://www.bbc.co.uk/search?q="
options[bing]="https://www.bing.com/search?q="
options[cliki]="https://www.cliki.net/site/search?query="
options[cnn]="https://www.cnn.com/search?q="
options[coinbase]="https://www.coinbase.com/price?query="
options[debianpkg]="https://packages.debian.org/search?suite=default&section=all&arch=any&searchon=names&keywords="
options[discogs]="https://www.discogs.com/search/?&type=all&q="
options[duckduckgo]="https://duckduckgo.com/?q="
options[ebay]="https://www.ebay.com/sch/i.html?&_nkw="
options[github]="https://github.com/search?q="
options[gitlab]="https://gitlab.com/search?search="
options[imdb]="https://www.imdb.com/find?q="
options[lbry]="https://lbry.tv/$/search?q="
options[odysee]="https://odysee.com/$/search?q="
options[reddit]="https://www.reddit.com/search/?q="
options[slashdot]="https://slashdot.org/index2.pl?fhfilter="
options[socialblade]="https://socialblade.com/youtube/user/"
options[sourceforge]="https://sourceforge.net/directory/?q="
options[stack]="https://stackoverflow.com/search?q="
options[startpage]="https://www.startpage.com/do/dsearch?query="
options[stockquote]="https://finance.yahoo.com/quote/"
options[thesaurus]="https://www.thesaurus.com/misspelling?term="
options[translate]="https://translate.google.com/?sl=auto&tl=en&text="
options[urban]="https://www.urbandictionary.com/define.php?term="
options[wayback]="https://web.archive.org/web/*/"
options[webster]="https://www.merriam-webster.com/dictionary/"
options[wikipedia]="https://en.wikipedia.org/wiki/"
options[wiktionary]="https://en.wiktionary.org/wiki/"
options[wolfram]="https://www.wolframalpha.com/input/?i="
options[youtube]="https://www.youtube.com/results?search_query="
options[google]="https://www.google.com/search?q="
options[googleimages]="https://www.google.com/search?hl=en&tbm=isch&q="
options[googlenews]="https://news.google.com/search?q="
options[googleSupport]="https://support.google.com/search?q="
options[googleSupportAdmin]="https://support.google.com/a/search?q="
options[googleStructuredData]="https://search.google.com/structured-data/testing-tool#url="
options[googleRichResults]="https://search.google.com/test/rich-results??url="
options[googlePagespeed]="https://developers.google.com/speed/pagespeed/insights/?url="
options[googleDevelopers]="https://developers.google.com/s/results?q="
options[googleOpenSource]="https://opensource.google/projects/search?q="
options[googleExperimentswithGoogle]="https://experiments.withgoogle.com/search?q="
options[googleDataset]="https://datasetsearch.research.google.com/search?query="
if [ -z "$1" ]; then
# Picking a search engine.
# shellcheck disable=SC2154
while [ -z "$engine" ]; do
engine=$(printf '%s\n' "${!options[@]}" | sort | dmenu -i -p 'Choose search engine:') "$@" || exit
url="${options["${engine}"]}" || exit
done
else
engine="$1"
fi
url="${options["${engine}"]}" || exit
# Searching the chosen engine.
# shellcheck disable=SC2154
while [ -z "$query" ]; do
query=$(echo "$engine" | dmenu -p 'Enter search query:') || exit
done
# Display search results in web browser
$DMBROWSER "$url""$query"

21
.local/bin/dmenuumount Executable file
View File

@ -0,0 +1,21 @@
#!/bin/sh
# A dmenu prompt to unmount drives.
# Provides you with mounted partitions, select one to unmount.
# Drives mounted at /, /boot and /home will not be options to unmount.
drives="$(lsblk -nrpo "name,type,size,mountpoint,label" | awk -F':' '{gsub(/ /,":")}$4!~/\/boot|\/efi|\/home$|SWAP/&&length($4)>1{printf "%s (%s) %s\n",$4,$3,$5}'; awk '/simple-mtpfs/ { print "📱", $2; }' /etc/mtab)"
chosen="$(echo "$drives" | dmenu -i -p "Unmount which drive?")" || exit 1
case "$chosen" in
📱*)
chosen="${chosen#📱 }"
sudo -A umount -l "$chosen"
;;
*)
chosen="${chosen% (*}"
sudo -A umount -l "$chosen"
;;
esac && notify-send "🖥️ Drive unmounted." "$chosen successfully unmounted." ||
notify-send "🖥️ Drive failed to unmount." "Possibly a permissions or I/O issue."

4
.local/bin/duck Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
url="https://lite.duckduckgo.com/lite?kd=-1&kp=-1&q=$(urlencode "$*")" # 🦆
exec lynx "$url"

45
.local/bin/ext Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh
# A general, all-purpose extraction script. Not all extraction programs here
# are installed by LARBS automatically.
#
# Default behavior: Extract archive into new directory
# Behavior with `-c` option: Extract contents into current directory
while getopts "hc" o; do case "${o}" in
c) extracthere="True" ;;
*) printf "Options:\\n -c: Extract archive into current directory rather than a new one.\\n" && exit 1 ;;
esac done
if [ -z "$extracthere" ]; then
archive="$(readlink -f "$*")" &&
directory="$(echo "$archive" | sed 's/\.[^\/.]*$//')" &&
mkdir -p "$directory" &&
cd "$directory" || exit 1
else
archive="$(readlink -f "$(echo "$*" | cut -d' ' -f2)" 2>/dev/null)"
fi
[ -z "$archive" ] && printf "Give archive to extract as argument.\\n" && exit 1
if [ -f "$archive" ] ; then
case "$archive" in
*.tar.bz2|*.tbz2) tar xvjf "$archive" ;;
*.tar.xz) tar -xf "$archive" ;;
*.tar.gz|*.tgz) tar xvzf "$archive" ;;
*.tar.zst) tar -I zstd -xf "$archive" ;;
*.lzma) unlzma "$archive" ;;
*.bz2) bunzip2 "$archive" ;;
*.rar) unrar x -ad "$archive" ;;
*.gz) gunzip "$archive" ;;
*.tar) tar xvf "$archive" ;;
*.zip) unzip "$archive" ;;
*.Z) uncompress "$archive" ;;
*.7z) 7z x "$archive" ;;
*.xz) unxz "$archive" ;;
*.exe) cabextract "$archive" ;;
*) printf "extract: '%s' - unknown archive method\\n" "$archive" ;;
esac
else
printf "File \"%s\" not found.\\n" "$archive"
fi

1449
.local/bin/fishies Executable file

File diff suppressed because it is too large Load Diff

12
.local/bin/getcomproot Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# A helper script for LaTeX/groff files used by `compiler` and `opout`.
# The user can add the root file of a larger project as a comment as below:
# % root = mainfile.tex
# And the compiler script will run on that instead of the opened file.
texroot="$(grep -i "^.\+\s*root\s*=\s*\S\+" "$1")"
texroot="${texroot##*=}"
texroot="${texroot//[\"\' ]}"
[ -f "$texroot" ] && readlink -f "$texroot" || exit 1

103
.local/bin/getnf Executable file
View File

@ -0,0 +1,103 @@
#!/bin/bash
#defining variables
nerdfontsrepo='https://api.github.com/repos/ryanoasis/nerd-fonts'
aFontInstalled="False"
keepArchives="False"
distDir="$HOME/.local/share/fonts"
downDir="$HOME/Downloads/NerdFonts"
os=$(uname)
# help message
usage() {
echo "getNF: A Better way to install NerdFonts"
echo ""
echo "Usage:"
echo "-h print this help message and exit"
echo "-k Keep the downloaded archives"
echo ""
}
# setting flags
while getopts :hk option; do
case "${option}" in
h) usage && exit 0 ;;
k) keepArchives="True" ;;
*) usage && exit 0
esac
done
# For Macs, need to set a few different things
if [[ "$os" == 'Darwin' ]]; then
distDir="$HOME/Library/Fonts"
fi
# Check if the distDir and downDir exists, if it doesn't, create it
[ -d "$distDir" ] || mkdir -p "$distDir" && echo "Fonts Directory exists, good"
[ -d "$downDir" ] || mkdir -p "$downDir" && echo "Download Fonts Directory exists, good"
# get font names
nerdFonts=$(curl --silent "$nerdfontsrepo/contents/patched-fonts?ref=master" | \
grep "name" | \
awk -F":" '{print $2}' | \
sed 's/["",]//g;/install\.ps1/d')
#get the latest release number from NerdFonts github repo
release=$(curl --silent "$nerdfontsrepo/releases/latest" | \
grep -Po '"tag_name": "\K.*?(?=")')
# use fzf to select the fonts to be installed
listFonts=$(printf '%s\n' "${nerdFonts[@]}" | fzf -m)
#loop over the selected fonts in listFonts, download and install them
for i in $listFonts; do
checkFont=$(fc-list | grep -i "$i")
if [ -z "$checkFont" ]; then #If the font already is installed, skip it
echo "$i font download started" &&
pushd "$downDir" > /dev/null &&
#download the font
curl -LJO -\# "https://github.com/ryanoasis/nerd-fonts/releases/download/$release/$i.zip" \
-o "$i.zip" --create-dirs &&
echo "$i font download finished" &&
echo "$i font unziping started" &&
#Unzipe the downloaded archive
# unzip -qq "$downDir/$i.zip" -d "$distDir" &&
unzip -qq "$i.zip" -d "$distDir" &&
echo "$i font unzipping finished" &&
echo "Font $i Installed" &&
installedFontName=$(curl --silent "$nerdfontsrepo/contents/patched-fonts/$i/Regular/complete?ref=master" | \
grep ".ttf" | \
awk -F ":" 'FNR == 1 {print $2}' | \
awk '{print $1}' | \
sed 's/"//g') &&
#set this variable to true so that the font cache get's updated
aFontInstalled=True && # We do this before the Additional info, so even if it fails the font cache will be refreshed
#Additional info, only if we can get the real font name
if [ -n "$installedFontName" ]; then
echo "$i provides:" &&
fc-list | grep -i "$installedFontName" | \
awk -F "/" '{print $7}' | \
sed 's/style\=//' | \
awk -F ":" 'BEGIN {print "FONT NAME" " | " "FILE NAME" " | " "STYLE"} {print $2 " | " $1 " | " $3}' | \
column -s "|" -t
fi
popd > /dev/null
else
echo "Font $i already installed"
fi
done
# If a font was installed, Update the font cache and remove the archive
if [ "$aFontInstalled" = "True" ]; then
echo "Regenerating fc-cache"
fc-cache -f 2>&1 && echo "fc-cache: regeneration succeeded!"
#check if the user hasn't chooen to keep the updated, if not, remove them
if [ "$keepArchives" = "False" ]; then
echo "Removing zip font files in $downDir" &&
rm $downDir/*.zip
else
echo "The archive files are in $downDir"
fi
fi
echo "All is done!"

4
.local/bin/google Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
url="https://google.com/search?q=$(urlencode "$*")"
exec lynx "https://google.com/search?q=$url"

12
.local/bin/ifinstalled Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
# Some optional functions in LARBS require programs not installed by default. I
# use this little script to check to see if a command exists and if it doesn't
# it informs the user that they need that command to continue. This is used in
# various other scripts for clarity's sake.
for x in "$@"; do
if ! which "$x" >/dev/null 2>&1 && ! pacman -Qq "$x" >/dev/null 2>&1; then
notify-send "📦 $x" "must be installed for this function." && exit 1 ;
fi
done

11
.local/bin/inc-sink-volume Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
default_loc=/tmp/pactl-default-sink
if [ ! -f "$default_loc" ] ; then
set-default-sink
fi
default=$(cat $default_loc)
pactl set-sink-volume $default +5%

11
.local/bin/inc-source-volume Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
default_loc=/tmp/pactl-default-source
if [ ! -f "$default_loc" ] ; then
set-default-source
fi
default=$(cat $default_loc)
pactl set-source-volume $default +5%

8
.local/bin/iommu.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
shopt -s nullglob
for g in /sys/kernel/iommu_groups/*; do
echo "IOMMU Group ${g##*/}:"
for d in $g/devices/*; do
echo -e "\t$(lspci -nns ${d##*/})"
done;
done;

2
.local/bin/isosec Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec date -u +%Y%m%d%H%M%S "$@"

26
.local/bin/kbswitcher Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env bash
fn="/tmp/kbswitcher.status"
layout=""
# first run the file won't exist
if [ ! -f "$fn" ]; then
layout="dvorak"
echo "dvorak" > "$fn"
else
case $(cat "$fn") in
us)
echo "dvorak" > "$fn"
layout="dvorak"
;;
dvorak)
echo "us" > "$fn"
layout="us"
;;
esac
fi
notify-send -u normal -r 161616 "kbswitcher" "Keyboard layout set to $layout."
setxkbmap -layout "$layout" -option ctrl:nocaps

24
.local/bin/lfub Executable file
View File

@ -0,0 +1,24 @@
#!/bin/sh
# This is a wrapper script for lb that allows it to create image previews with
# ueberzug. This works in concert with the lf configuration file and the
# lf-cleaner script.
set -e
cleanup() {
exec 3>&-
rm "$FIFO_UEBERZUG"
}
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
lf "$@"
else
[ ! -d "$HOME/.cache/lf" ] && mkdir -p "$HOME/.cache/lf"
export FIFO_UEBERZUG="$HOME/.cache/lf/ueberzug-$$"
mkfifo "$FIFO_UEBERZUG"
ueberzug layer -s <"$FIFO_UEBERZUG" -p json &
exec 3>"$FIFO_UEBERZUG"
trap cleanup HUP INT QUIT TERM PWR EXIT
lf "$@" 3>&-
fi

23
.local/bin/lynx Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# be sure to install the ca-certificates package
lynxpath=/usr/bin/lynx
[[ ! -x $lynxpath ]] && lynxpath=/usr/local/bin/lynx
useragent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.79 Safari/537.1 Lynx"
if [ -e "$HOME/.config/lynx/lynx.cfg" ];then
export LYNX_CFG="$HOME/.config/lynx/lynx.cfg"
fi
if [ -e "$HOME/.config/lynx/lynx.lss" ];then
export LYNX_LSS="$HOME/.config/lynx/lynx.lss"
fi
if [ ! -x "$lynxpath" ]; then
echo "Doesn't look like lynx is installed."
exit 1
fi
exec "$lynxpath" --useragent="$useragent" "$@"

18
.local/bin/maimpick Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
# This is bound to Shift+PrintScreen by default, requires maim. It lets you
# choose the kind of screenshot to take, including copying the image or even
# highlighting an area to copy. scrotcucks on suicidewatch right now.
# variables
output="$(date '+%y%m%d-%H%M-%S').png"
xclip_cmd="xclip -sel clip -t image/png"
case "$(printf "a selected area\\ncurrent window\\nfull screen\\na selected area (copy)\\ncurrent window (copy)\\nfull screen (copy)" | dmenu -l 6 -i -p "Screenshot which area?")" in
"a selected area") maim -s pic-selected-"${output}" ;;
"current window") maim -q -d 0.2 -i "$(xdotool getactivewindow)" pic-window-"${output}" ;;
"full screen") maim -q -d 0.2 pic-full-"${output}" ;;
"a selected area (copy)") maim -s | ${xclip_cmd} ;;
"current window (copy)") maim -q -d 0.2 -i "$(xdotool getactivewindow)" | ${xclip_cmd} ;;
"full screen (copy)") maim -q -d 0.2 | ${xclip_cmd} ;;
esac

19
.local/bin/mod_backlight Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
if command -v brillo &> /dev/null; then
function send_notification() {
brightness=$(printf "%.0f\n" $(brillo -G))
dunstify -a "changebrightness" -u low -r 9991 -h int:value:"$brightness" -i "brightness-$1" "Brightness: $brightness%" -t 2000
}
case $1 in
up)
brillo -A 5 -q -u 150000
send_notification $1
;;
down)
brillo -U 5 -q -u 150000
send_notification $1
;;
esac
fi

924
.local/bin/networkmanager_dmenu Executable file
View File

@ -0,0 +1,924 @@
#!/usr/bin/env python3
# encoding:utf8
"""NetworkManager command line dmenu script.
To add new connections or enable/disable networking requires policykit
permissions setup per:
https://wiki.archlinux.org/index.php/NetworkManager#Set_up_PolicyKit_permissions
OR running the script as root
Add dmenu options and default terminal if desired to
~/.config/networkmanager-dmenu/config.ini
"""
import pathlib
import struct
import configparser
import locale
import os
from os.path import expanduser
import shlex
from shutil import which
import sys
from time import sleep
import uuid
import subprocess
# pylint: disable=import-error
import gi
gi.require_version('NM', '1.0')
from gi.repository import GLib, NM # noqa pylint: disable=wrong-import-position
# pylint: enable=import-error
ENV = os.environ.copy()
ENV['LC_ALL'] = 'C'
ENC = locale.getpreferredencoding()
CONF = configparser.ConfigParser()
CONF.read(expanduser("~/.config/networkmanager-dmenu/config.ini"))
def cli_args():
""" Don't override dmenu_cmd function arguments with CLI args. Removes -l
and -p if those are passed on the command line.
Exception: if -l is passed and dmenu_command is not defined, assume that the
user wants to switch dmenu to the vertical layout and include -l.
Returns: List of additional CLI arguments
"""
args = sys.argv[1:]
cmd = CONF.get('dmenu', 'dmenu_command', fallback=False)
if "-l" in args or "-p" in args:
for nope in ['-l', '-p'] if cmd is not False else ['-p']:
try:
nope_idx = args.index(nope)
del args[nope_idx]
del args[nope_idx]
except ValueError:
pass
return args
def dmenu_pass(command, color):
"""Check if dmenu passphrase patch is applied and return the correct command
line arg list
Args: command - string
color - obscure color string
Returns: list or None
"""
if command != 'dmenu':
return None
try:
# Check for dmenu password patch
dm_patch = b'P' in subprocess.run(["dmenu", "-h"],
capture_output=True,
check=False).stderr
except FileNotFoundError:
dm_patch = False
return ["-P"] if dm_patch else ["-nb", color, "-nf", color]
def dmenu_cmd(num_lines, prompt="Networks", active_lines=None):
"""Parse config.ini for menu options
Args: args - num_lines: number of lines to display
prompt: prompt to show
active_lines: list of line numbers to tag as active
Returns: command invocation (as a list of strings) for example
["dmenu", "-l", "<num_lines>", "-p", "<prompt>", "-i"]
"""
# Create command string
commands = {"dmenu": ["-p", str(prompt)],
"rofi": ["-dmenu", "-p", str(prompt), "-l", str(num_lines)],
"bemenu": ["-p", str(prompt)],
"wofi": ["-p", str(prompt)]}
command = shlex.split(CONF.get('dmenu', 'dmenu_command', fallback="dmenu"))
command.extend(cli_args())
command.extend(commands.get(command[0], []))
# Rofi Highlighting
rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False)
if rofi_highlight is True and command[0] == "rofi" and active_lines:
command.extend(["-a", ",".join([str(num) for num in active_lines])])
# Passphrase prompts
obscure = CONF.getboolean('dmenu_passphrase', 'obscure', fallback=False)
if prompt == "Passphrase" and obscure is True:
obscure_color = CONF.get('dmenu_passphrase', 'obscure_color', fallback='#222222')
pass_prompts = {"dmenu": dmenu_pass(command[0], obscure_color),
"rofi": ['-password'],
"bemenu": ['-x'],
"wofi": ['-P']}
command.extend(pass_prompts.get(command[0], []))
return command
def choose_adapter(client):
"""If there is more than one wifi adapter installed, ask which one to use
"""
devices = client.get_devices()
devices = [i for i in devices if i.get_device_type() == NM.DeviceType.WIFI]
if not devices:
return None
if len(devices) == 1:
return devices[0]
device_names = "\n".join([d.get_iface() for d in devices])
sel = subprocess.run(dmenu_cmd(len(devices), "CHOOSE ADAPTER:"),
capture_output=True,
check=False,
env=ENV,
input=device_names,
encoding=ENC).stdout
if not sel.strip():
sys.exit()
devices = [i for i in devices if i.get_iface() == sel.strip()]
assert len(devices) == 1
return devices[0]
def is_installed(cmd):
"""Check if a utility is installed"""
return which(cmd) is not None
def bluetooth_get_enabled():
"""Check if bluetooth is enabled via rfkill.
Returns None if no bluetooth device was found.
"""
# See https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-rfkill
for path in pathlib.Path('/sys/class/rfkill/').glob('rfkill*'):
if (path / 'type').read_text().strip() == 'bluetooth':
return (path / 'soft').read_text().strip() == '0'
return None
def create_other_actions(client):
"""Return list of other actions that can be taken
"""
networking_enabled = client.networking_get_enabled()
networking_action = "Disable" if networking_enabled else "Enable"
wifi_enabled = client.wireless_get_enabled()
wifi_action = "Disable" if wifi_enabled else "Enable"
bluetooth_enabled = bluetooth_get_enabled()
bluetooth_action = "Disable" if bluetooth_enabled else "Enable"
actions = [Action(f"{wifi_action} Wifi", toggle_wifi,
not wifi_enabled),
Action(f"{networking_action} Networking",
toggle_networking, not networking_enabled)]
if bluetooth_enabled is not None:
actions.append(Action(f"{bluetooth_action} Bluetooth",
toggle_bluetooth, not bluetooth_enabled))
actions += [Action("Launch Connection Manager", launch_connection_editor),
Action("Delete a Connection", delete_connection)]
if wifi_enabled:
actions.append(Action("Rescan Wifi Networks", rescan_wifi))
return actions
def rescan_wifi():
"""
Rescan Wifi Access Points
"""
delay = CONF.getint('nmdm', 'rescan_delay', fallback=5)
for dev in CLIENT.get_devices():
if gi.repository.NM.DeviceWifi == type(dev):
try:
dev.request_scan_async(None, rescan_cb, None)
LOOP.run()
sleep(delay)
notify("Wifi scan complete")
main()
except gi.repository.GLib.Error as err:
# Too frequent rescan error
notify("Wifi rescan failed", urgency="critical")
if not err.code == 6: # pylint: disable=no-member
raise err
def rescan_cb(dev, res, data):
"""Callback for rescan_wifi. Just for notifications
"""
if dev.request_scan_finish(res) is True:
notify("Wifi scan running...")
else:
notify("Wifi scan failed", urgency="critical")
LOOP.quit()
def ssid_to_utf8(nm_ap):
""" Convert binary ssid to utf-8 """
ssid = nm_ap.get_ssid()
if not ssid:
return ""
ret = NM.utils_ssid_to_utf8(ssid.get_data())
return ret
def prompt_saved(saved_cons):
"""Prompt for a saved connection."""
actions = create_saved_actions(saved_cons)
sel = get_selection(actions)
sel()
def ap_security(nm_ap):
"""Parse the security flags to return a string with 'WPA2', etc. """
flags = nm_ap.get_flags()
wpa_flags = nm_ap.get_wpa_flags()
rsn_flags = nm_ap.get_rsn_flags()
sec_str = ""
if ((flags & getattr(NM, '80211ApFlags').PRIVACY) and
(wpa_flags == 0) and (rsn_flags == 0)):
sec_str = " WEP"
if wpa_flags:
sec_str = " WPA1"
if rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_PSK:
sec_str += " WPA2"
if rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_SAE:
sec_str += " WPA3"
if ((wpa_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_802_1X) or
(rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_802_1X)):
sec_str += " 802.1X"
if ((wpa_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_OWE) or
(rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_OWE)):
sec_str += " OWE"
# If there is no security use "--"
if sec_str == "":
sec_str = "--"
return sec_str.lstrip()
class Action(): # pylint: disable=too-few-public-methods
"""Helper class to execute functions from a string variable"""
def __init__(self,
name,
func,
args=None,
active=False):
self.name = name
self.func = func
self.is_active = active
if args is None:
self.args = None
elif isinstance(args, list):
self.args = args
else:
self.args = [args]
def __str__(self):
return self.name
def __call__(self):
if self.args is None:
self.func()
else:
self.func(*self.args)
def conn_matches_adapter(conn, adapter):
"""Return True if the connection is applicable for the given adapter.
There seem to be two ways for a connection specify what interface it belongs
to:
- By setting 'mac-address' in [wifi] to the adapter's MAC
- By setting 'interface-name` in [connection] to the adapter's name.
Depending on how the connection was added, it seems like either
'mac-address', 'interface-name' or neither of both is set.
"""
# [wifi] mac-address
setting_wireless = conn.get_setting_wireless()
mac = setting_wireless.get_mac_address()
if mac is not None:
return mac == adapter.get_permanent_hw_address()
# [connection] interface-name
setting_connection = conn.get_setting_connection()
interface = setting_connection.get_interface_name()
if interface is not None:
return interface == adapter.get_iface()
# Neither is set, let's assume this connection is for multiple/all adapters.
return True
def process_ap(nm_ap, is_active, adapter):
"""Activate/Deactivate a connection and get password if required"""
if is_active:
CLIENT.deactivate_connection_async(nm_ap, None, deactivate_cb, nm_ap)
LOOP.run()
else:
conns_cur = [i for i in CONNS if
i.get_setting_wireless() is not None and
conn_matches_adapter(i, adapter)]
con = nm_ap.filter_connections(conns_cur)
if len(con) > 1:
raise ValueError("There are multiple connections possible")
if len(con) == 1:
CLIENT.activate_connection_async(con[0], adapter, nm_ap.get_path(),
None, activate_cb, nm_ap)
LOOP.run()
else:
if ap_security(nm_ap) != "--":
password = get_passphrase()
else:
password = ""
set_new_connection(nm_ap, password, adapter)
def activate_cb(dev, res, data):
"""Notification if activate connection completed successfully
"""
try:
conn = dev.activate_connection_finish(res)
except GLib.Error:
conn = None
if conn is not None:
notify(f"Activated {conn.get_id()}")
else:
notify(f"Problem activating {data.get_id()}", urgency="critical")
LOOP.quit()
def deactivate_cb(dev, res, data):
"""Notification if deactivate connection completed successfully
"""
if dev.deactivate_connection_finish(res) is True:
notify(f"Deactivated {data.get_id()}")
else:
notify(f"Problem deactivating {data.get_id()}", urgency="critical")
LOOP.quit()
def process_vpngsm(con, activate):
"""Activate/deactive VPN or GSM connections"""
if activate:
CLIENT.activate_connection_async(con, None, None,
None, activate_cb, con)
else:
CLIENT.deactivate_connection_async(con, None, deactivate_cb, con)
LOOP.run()
def create_ap_actions(aps, active_ap, active_connection, adapter): # noqa pylint: disable=too-many-locals,line-too-long
"""For each AP in a list, create the string and its attached function
(activate/deactivate)
"""
active_ap_bssid = active_ap.get_bssid() if active_ap is not None else ""
names = [ssid_to_utf8(ap) for ap in aps]
max_len_name = max([len(name) for name in names]) if names else 0
secs = [ap_security(ap) for ap in aps]
max_len_sec = max([len(sec) for sec in secs]) if secs else 0
ap_actions = []
for nm_ap, name, sec in zip(aps, names, secs):
bars = NM.utils_wifi_strength_bars(nm_ap.get_strength())
wifi_chars = CONF.get("dmenu", "wifi_chars", fallback=False)
if wifi_chars:
bars = "".join([wifi_chars[i] for i, j in enumerate(bars) if j == '*'])
is_active = nm_ap.get_bssid() == active_ap_bssid
compact = CONF.getboolean("dmenu", "compact", fallback=False)
if compact:
action_name = f"{name} {sec} {bars}"
else:
action_name = f"{name:<{max_len_name}s} {sec:<{max_len_sec}s} {bars:>4}"
if is_active:
ap_actions.append(Action(action_name, process_ap,
[active_connection, True, adapter],
active=True))
else:
ap_actions.append(Action(action_name, process_ap,
[nm_ap, False, adapter]))
return ap_actions
def create_vpn_actions(vpns, active):
"""Create the list of strings to display with associated function
(activate/deactivate) for VPN connections.
"""
active_vpns = [i for i in active if i.get_vpn()]
return _create_vpngsm_actions(vpns, active_vpns, "VPN")
def create_wireguard_actions(wgs, active):
"""Create the list of strings to display with associated function
(activate/deactivate) for Wireguard connections.
"""
active_wgs = [i for i in active if i.get_connection_type() == "wireguard"]
return _create_vpngsm_actions(wgs, active_wgs, "Wireguard")
def create_eth_actions(eths, active):
"""Create the list of strings to display with associated function
(activate/deactivate) for Ethernet connections.
"""
active_eths = [i for i in active if 'ethernet' in i.get_connection_type()]
return _create_vpngsm_actions(eths, active_eths, "Eth")
def create_gsm_actions(gsms, active):
"""Create the list of strings to display with associated function
(activate/deactivate) GSM connections."""
active_gsms = [i for i in active if
i.get_connection() is not None and
i.get_connection().is_type(NM.SETTING_GSM_SETTING_NAME)]
return _create_vpngsm_actions(gsms, active_gsms, "GSM")
def create_blue_actions(blues, active):
"""Create the list of strings to display with associated function
(activate/deactivate) Bluetooth connections."""
active_blues = [i for i in active if
i.get_connection() is not None and
i.get_connection().is_type(NM.SETTING_BLUETOOTH_SETTING_NAME)]
return _create_vpngsm_actions(blues, active_blues, "Bluetooth")
def create_saved_actions(saved):
"""Create the list of strings to display with associated function
(activate/deactivate) for VPN connections.
"""
return _create_vpngsm_actions(saved, [], "SAVED")
def _create_vpngsm_actions(cons, active_cons, label):
active_con_ids = [a.get_id() for a in active_cons]
actions = []
for con in cons:
is_active = con.get_id() in active_con_ids
action_name = f"{con.get_id()}:{label}"
if is_active:
active_connection = [a for a in active_cons
if a.get_id() == con.get_id()]
if len(active_connection) != 1:
raise ValueError(f"Multiple active connections match {con.get_id()}")
active_connection = active_connection[0]
actions.append(Action(action_name, process_vpngsm,
[active_connection, False], active=True))
else:
actions.append(Action(action_name, process_vpngsm,
[con, True]))
return actions
def create_wwan_actions(client):
"""Create WWWAN actions
"""
wwan_enabled = client.wwan_get_enabled()
wwan_action = "Disable" if wwan_enabled else "Enable"
return [Action(f"{wwan_action} WWAN", toggle_wwan, not wwan_enabled)]
def combine_actions(eths, aps, vpns, wgs, gsms, blues, wwan, others, saved):
# pylint: disable=too-many-arguments
"""Combine all given actions into a list of actions.
Args: args - eths: list of Actions
aps: list of Actions
vpns: list of Actions
gsms: list of Actions
blues: list of Actions
wwan: list of Actions
others: list of Actions
"""
compact = CONF.getboolean("dmenu", "compact", fallback=False)
empty_action = [Action('', None)] if not compact else []
all_actions = []
all_actions += eths + empty_action if eths else []
all_actions += aps + empty_action if aps else []
all_actions += vpns + empty_action if vpns else []
all_actions += wgs + empty_action if wgs else []
all_actions += gsms + empty_action if (gsms and wwan) else []
all_actions += blues + empty_action if blues else []
all_actions += wwan + empty_action if wwan else []
all_actions += others + empty_action if others else []
all_actions += saved + empty_action if saved else []
return all_actions
def get_selection(all_actions):
"""Spawn dmenu for selection and execute the associated action."""
rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False)
inp = []
if rofi_highlight is True:
inp = [str(action) for action in all_actions]
else:
inp = [('== ' if action.is_active else ' ') + str(action)
for action in all_actions]
active_lines = [index for index, action in enumerate(all_actions)
if action.is_active]
command = dmenu_cmd(len(inp), active_lines=active_lines)
sel = subprocess.run(command,
capture_output=True,
check=False,
input="\n".join(inp),
encoding=ENC,
env=ENV).stdout
if not sel.rstrip():
sys.exit()
if rofi_highlight is False:
action = [i for i in all_actions
if ((str(i).strip() == str(sel.strip())
and not i.is_active) or
('== ' + str(i) == str(sel.rstrip('\n'))
and i.is_active))]
else:
action = [i for i in all_actions if str(i).strip() == sel.strip()]
assert len(action) == 1, f"Selection was ambiguous: '{str(sel.strip())}'"
return action[0]
def toggle_networking(enable):
"""Enable/disable networking
Args: enable - boolean
"""
toggle = GLib.Variant.new_tuple(GLib.Variant.new_boolean(enable))
try:
CLIENT.dbus_call(NM.DBUS_PATH, NM.DBUS_INTERFACE, "Enable", toggle,
None, -1, None, None, None)
except AttributeError:
# Workaround for older versions of python-gobject
CLIENT.networking_set_enabled(enable)
notify(f"Networking {'enabled' if enable is True else 'disabled'}")
def toggle_wifi(enable):
"""Enable/disable Wifi
Args: enable - boolean
"""
toggle = GLib.Variant.new_boolean(enable)
try:
CLIENT.dbus_set_property(NM.DBUS_PATH, NM.DBUS_INTERFACE, "WirelessEnabled", toggle,
-1, None, None, None)
except AttributeError:
# Workaround for older versions of python-gobject
CLIENT.wireless_set_enabled(enable)
notify(f"Wifi {'enabled' if enable is True else 'disabled'}")
def toggle_wwan(enable):
"""Enable/disable WWAN
Args: enable - boolean
"""
toggle = GLib.Variant.new_boolean(enable)
try:
CLIENT.dbus_set_property(NM.DBUS_PATH, NM.DBUS_INTERFACE, "WwanEnabled", toggle,
-1, None, None, None)
except AttributeError:
# Workaround for older versions of python-gobject
CLIENT.wwan_set_enabled(enable)
notify(f"Wwan {'enabled' if enable is True else 'disabled'}")
def toggle_bluetooth(enable):
"""Enable/disable Bluetooth
Args: enable - boolean
References:
https://github.com/blueman-project/blueman/blob/master/blueman/plugins/mechanism/RfKill.py
https://www.kernel.org/doc/html/latest/driver-api/rfkill.html
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/linux/rfkill.h?h=v5.8.9
"""
type_bluetooth = 2
op_change_all = 3
idx = 0
soft_state = 0 if enable else 1
hard_state = 0
data = struct.pack("IBBBB", idx, type_bluetooth, op_change_all,
soft_state, hard_state)
try:
with open('/dev/rfkill', 'r+b', buffering=0) as rff:
rff.write(data)
except PermissionError:
notify("Lacking permission to write to /dev/rfkill.",
"Check README for configuration options.",
urgency="critical")
else:
notify(f"Bluetooth {'enabled' if enable else 'disabled'}")
def launch_connection_editor():
"""Launch nmtui or the gui nm-connection-editor
"""
terminal = CONF.get("editor", "terminal", fallback="xterm")
gui_if_available = CONF.getboolean("editor", "gui_if_available", fallback=True)
guis = ["gnome-control-center", "nm-connection-editor"]
if gui_if_available is True:
for gui in guis:
if is_installed(gui):
subprocess.run(gui, check=False)
return
if is_installed("nmtui"):
subprocess.run([terminal, "-e", "nmtui"], check=False)
return
notify("No network connection editor installed", urgency="critical")
def get_passphrase():
"""Get a password
Returns: string
"""
pinentry = CONF.get("dmenu", "pinentry", fallback=None)
if pinentry:
pin = ""
out = subprocess.run(pinentry,
capture_output=True,
check=False,
encoding=ENC,
input='setdesc Get network password\ngetpin\n').stdout
if out:
res = out.split("\n")[2]
if res.startswith("D "):
pin = res.split("D ")[1]
return pin
return subprocess.run(dmenu_cmd(0, "Passphrase"),
stdin=subprocess.DEVNULL,
capture_output=True,
check=False,
encoding=ENC).stdout
def delete_connection():
"""Display list of NM connections and delete the selected one
"""
conn_acts = [Action(i.get_id(), i.delete_async, args=[None, delete_cb, None]) for i in CONNS]
conn_names = "\n".join([str(i) for i in conn_acts])
sel = subprocess.run(dmenu_cmd(len(conn_acts), "CHOOSE CONNECTION TO DELETE:"),
capture_output=True,
check=False,
input=conn_names,
encoding=ENC,
env=ENV).stdout
if not sel.strip():
sys.exit()
action = [i for i in conn_acts if str(i) == sel.rstrip("\n")]
assert len(action) == 1, f"Selection was ambiguous: {str(sel)}"
action[0]()
LOOP.run()
def delete_cb(dev, res, data):
"""Notification if delete completed successfully
"""
if dev.delete_finish(res) is True:
notify(f"Deleted {dev.get_id()}")
else:
notify(f"Problem deleting {dev.get_id()}", urgency="critical")
LOOP.quit()
def set_new_connection(nm_ap, nm_pw, adapter):
"""Setup a new NetworkManager connection
Args: ap - NM.AccessPoint
pw - string
"""
nm_pw = str(nm_pw).strip()
profile = create_wifi_profile(nm_ap, nm_pw, adapter)
CLIENT.add_and_activate_connection_async(profile, adapter, nm_ap.get_path(),
None, verify_conn, profile)
LOOP.run()
def create_wifi_profile(nm_ap, password, adapter):
# pylint: disable=line-too-long
# noqa From https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/gi/add_connection.py
# noqa and https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/dbus/add-wifi-psk-connection.py
# pylint: enable=line-too-long
"""Create the NM profile given the AP and passphrase"""
ap_sec = ap_security(nm_ap)
profile = NM.SimpleConnection.new()
s_con = NM.SettingConnection.new()
s_con.set_property(NM.SETTING_CONNECTION_ID, ssid_to_utf8(nm_ap))
s_con.set_property(NM.SETTING_CONNECTION_UUID, str(uuid.uuid4()))
s_con.set_property(NM.SETTING_CONNECTION_TYPE, "802-11-wireless")
profile.add_setting(s_con)
s_wifi = NM.SettingWireless.new()
s_wifi.set_property(NM.SETTING_WIRELESS_SSID, nm_ap.get_ssid())
s_wifi.set_property(NM.SETTING_WIRELESS_MODE, 'infrastructure')
s_wifi.set_property(NM.SETTING_WIRELESS_MAC_ADDRESS, adapter.get_permanent_hw_address())
profile.add_setting(s_wifi)
s_ip4 = NM.SettingIP4Config.new()
s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto")
profile.add_setting(s_ip4)
s_ip6 = NM.SettingIP6Config.new()
s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto")
profile.add_setting(s_ip6)
if ap_sec != "--":
s_wifi_sec = NM.SettingWirelessSecurity.new()
if "WPA" in ap_sec:
if "WPA3" in ap_sec:
s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT,
"sae")
else:
s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT,
"wpa-psk")
s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_AUTH_ALG,
"open")
s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_PSK, password)
elif "WEP" in ap_sec:
s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT,
"None")
s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
NM.WepKeyType.PASSPHRASE)
s_wifi_sec.set_wep_key(0, password)
profile.add_setting(s_wifi_sec)
return profile
def verify_conn(client, result, data):
"""Callback function for add_and_activate_connection_async
Check if connection completes successfully. Delete the connection if there
is an error.
"""
try:
act_conn = client.add_and_activate_connection_finish(result)
conn = act_conn.get_connection()
if not all([conn.verify(),
conn.verify_secrets(),
data.verify(),
data.verify_secrets()]):
raise GLib.Error
notify(f"Added {conn.get_id()}")
except GLib.Error:
try:
notify(f"Connection to {conn.get_id()} failed",
urgency="critical")
conn.delete_async(None, None, None)
except UnboundLocalError:
pass
finally:
LOOP.quit()
def create_ap_list(adapter, active_connections):
"""Generate list of access points. Remove duplicate APs , keeping strongest
ones and the active AP
Args: adapter
active_connections - list of all active connections
Returns: aps - list of access points
active_ap - active AP
active_ap_con - active Connection
adapter
"""
aps = []
ap_names = []
active_ap = adapter.get_active_access_point()
aps_all = sorted(adapter.get_access_points(),
key=lambda a: a.get_strength(), reverse=True)
conns_cur = [i for i in CONNS if
i.get_setting_wireless() is not None and
conn_matches_adapter(i, adapter)]
try:
ap_conns = active_ap.filter_connections(conns_cur)
active_ap_name = ssid_to_utf8(active_ap)
active_ap_con = [active_conn for active_conn in active_connections
if active_conn.get_connection() in ap_conns]
except AttributeError:
active_ap_name = None
active_ap_con = []
if len(active_ap_con) > 1:
raise ValueError("Multiple connection profiles match"
" the wireless AP")
active_ap_con = active_ap_con[0] if active_ap_con else None
for nm_ap in aps_all:
ap_name = ssid_to_utf8(nm_ap)
if nm_ap != active_ap and ap_name == active_ap_name:
# Skip adding AP if it's not active but same name as active AP
continue
if ap_name not in ap_names:
ap_names.append(ap_name)
aps.append(nm_ap)
return aps, active_ap, active_ap_con, adapter
def notify(message, details=None, urgency="low"):
"""Use notify-send if available for notifications
"""
delay = CONF.getint('nmdm', 'rescan_delay', fallback=5)
args = ["-u", urgency, "-a", "networkmanager-dmenu",
"-t", str(delay * 1000), message]
if details is not None:
args.append(details)
if is_installed("notify-send"):
subprocess.run(["notify-send"] + args, check=False)
def run(): # pylint: disable=too-many-locals
"""Main script entrypoint"""
active = CLIENT.get_active_connections()
adapter = choose_adapter(CLIENT)
if adapter:
ap_actions = create_ap_actions(*create_ap_list(adapter, active))
else:
ap_actions = []
vpns = [i for i in CONNS if i.is_type(NM.SETTING_VPN_SETTING_NAME)]
try:
wgs = [i for i in CONNS if i.is_type(NM.SETTING_WIREGUARD_SETTING_NAME)]
except AttributeError:
# Workaround for older versions of python-gobject with no wireguard support
wgs = []
eths = [i for i in CONNS if i.is_type(NM.SETTING_WIRED_SETTING_NAME)]
blues = [i for i in CONNS if i.is_type(NM.SETTING_BLUETOOTH_SETTING_NAME)]
vpn_actions = create_vpn_actions(vpns, active)
wg_actions = create_wireguard_actions(wgs, active)
eth_actions = create_eth_actions(eths, active)
blue_actions = create_blue_actions(blues, active)
other_actions = create_other_actions(CLIENT)
wwan_installed = is_installed("ModemManager")
if wwan_installed:
gsms = [i for i in CONNS if i.is_type(NM.SETTING_GSM_SETTING_NAME)]
gsm_actions = create_gsm_actions(gsms, active)
wwan_actions = create_wwan_actions(CLIENT)
else:
gsm_actions = []
wwan_actions = []
list_saved = CONF.getboolean('dmenu', 'list_saved', fallback=False)
saved_cons = [i for i in CONNS if i not in vpns + wgs + eths + blues]
if list_saved:
saved_actions = create_saved_actions(saved_cons)
else:
saved_actions = [Action("Saved connections", prompt_saved, [saved_cons])]
actions = combine_actions(eth_actions, ap_actions, vpn_actions, wg_actions,
gsm_actions, blue_actions, wwan_actions,
other_actions, saved_actions)
sel = get_selection(actions)
sel()
def main():
"""Main. Enables script to be re-run after a wifi rescan
"""
global CLIENT, CONNS, LOOP # noqa pylint: disable=global-variable-undefined
CLIENT = NM.Client.new(None)
LOOP = GLib.MainLoop()
CONNS = CLIENT.get_connections()
run()
if __name__ == '__main__':
main()
# vim: set et ts=4 sw=4 :

13
.local/bin/opout Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
# opout: "open output": A general handler for opening a file's intended output,
# usually the pdf of a compiled document. I find this useful especially
# running from vim.
basename="${1%.*}"
case "${*}" in
*.tex|*.sil|*.m[dse]|*.[rR]md|*.mom|*.[0-9]) target="$(getcomproot "$1" || echo "$1")" ; setsid -f xdg-open "${target%.*}".pdf >/dev/null 2>&1 ;;
*.html) setsid -f "$BROWSER" "$basename".html >/dev/null 2>&1 ;;
*.sent) setsid -f sent "$1" >/dev/null 2>&1 ;;
esac

12
.local/bin/rotdir Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
# When I open an image from the file manager in sxiv (the image viewer), I want
# to be able to press the next/previous keys to key through the rest of the
# images in the same directory. This script "rotates" the content of a
# directory based on the first chosen file, so that if I open the 15th image,
# if I press next, it will go to the 16th etc. Autistic, I know, but this is
# one of the reasons that sxiv is great for being able to read standard input.
[ -z "$1" ] && echo "usage: rotdir regex 2>&1" && exit 1
base="$(basename "$1")"
ls "$PWD" | awk -v BASE="$base" 'BEGIN { lines = ""; m = 0; } { if ($0 == BASE) { m = 1; } } { if (!m) { if (lines) { lines = lines"\n"; } lines = lines""$0; } else { print $0; } } END { print lines; }'

18
.local/bin/rssadd Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
if echo "$1" | grep -q "https*://\S\+\.[A-Za-z]\+\S*" ; then
url="$1"
else
url="$(grep -Eom1 '<[^>]+(rel="self"|application/[a-z]+\+xml)[^>]+>' "$1" |
grep -o "https?://[^\" ]")"
echo "$url" | grep -q "https*://\S\+\.[A-Za-z]\+\S*" ||
notify-send "That doesn't look like a full URL." && exit 1
fi
RSSFILE="${XDG_CONFIG_HOME:-$HOME/.config}/newsboat/urls"
if awk '{print $1}' "$RSSFILE" | grep "^$url$" >/dev/null; then
notify-send "You already have this RSS feed."
else
echo "$url" >> "$RSSFILE" && notify-send "RSS feed added."
fi

16
.local/bin/screenlayout Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
layouts=($(ls "$HOME/.screenlayout/"))
add="add layout..."
layouts+=("$add")
choice=$( printf '%s\n' "${layouts[@]}" | dmenu -i -p 'Choose a screenlayout:') "$@" || exit
if [ "$choice" = "$add" ]
then
arandr &
else
exec "$HOME/.screenlayout/$choice"
fi

13
.local/bin/screenshot Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
SCRIPTNAME=$(basename $0)
FILENAME="$HOME/$SCRIPTNAME-$(date +'%Y-%m-%d-%H-%M-%S').png"
case ${1:-} in
select*|region|area) SEL="-s" ;;
*) SEL="" ;;
esac
maim --format=png $SEL "$FILENAME"
echo -n $FILENAME | xclip -selection clipbard
notify-send "Screenshot" "$(echo -e "Screen shot saved\n$FILENAME")"

13
.local/bin/set-default-sink Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
choice=$(pactl list short sinks | awk '{print $2}' | dmenu -i -p "which sink should be default?")
if [ -z "$choice" ] ; then
exit 0;
fi
rm /tmp/pactl-default-sink --force # to ignore if it isn't there
touch /tmp/pactl-default-sink
echo "$choice" > /tmp/pactl-default-sink
pactl set-default-sink $choice # make sure that the chosen sink is the

13
.local/bin/set-default-source Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
choice=$(pactl list short sources | awk '{print $2}' | dmenu -i -p "which source should be default?")
if [ -z "$choice" ] ; then
exit 0;
fi
rm /tmp/pactl-default-source --force # to ignore if it isn't there
touch /tmp/pactl-default-source
echo "$choice" > /tmp/pactl-default-source
pactl set-default-source $choice # make sure that the chosen sink is the

13
.local/bin/setvol Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
default_loc=/tmp/pactl-default-sink
if [ ! -f "$default_loc" ] ; then
set-default-sink
fi
default=$(cat $default_loc)
value=$(echo -e "" | dmenu -bw 0 -i -p "Set vol to what?")
pactl set-sink-volume $default $value%

44
.local/bin/status-bat Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
icon=""
status=$(bash-status-bat)
parts=($status)
current=$(echo "${parts[0]}" | sed 's/\%//')
state=${parts[1]}
status="$current%"
if [[ "$state" == "discharging" ]]; then
case $(((current/20)+1)) in
1)
icon=" "
;;
2)
icon=" "
;;
3)
icon=" "
;;
4)
icon=" "
;;
5)
icon=" "
;;
esac
fi
if [[ "$state" == "not-charging" ]]; then
icon=" "
fi
if [[ "$state" == "fully-charged" ]]; then
icon=" "
status="Full"
fi
if [[ "$state" == "charging" ]]; then
icon=" "
fi
echo "$icon $status"

10
.local/bin/status-disk Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
freemb=$(df -h -B 1048576 | grep "/$" | awk -F ' ' '{ print $4 }')
freegb=$(df -h -B 1048576 | grep "/$" | awk -F ' ' '{ print $4/1024 }')
if [ $freemb -lt 1024 ]; then
printf " %0.2fMb" $freemb
else
printf " %0.2fGb" $freegb
fi

61
.local/bin/status-net Executable file
View File

@ -0,0 +1,61 @@
#!/bin/sh
dev_wifi=$(cat "$HOME"/.config/net-cfg/dev_wifi)
dev_eth=$(cat "$HOME"/.config/net-cfg/dev_eth)
dev_vpn=$(cat "$HOME"/.config/net-cfg/dev_vpn)
base03=#002b36
base02=#073642
base01=#586e75
base00=#657b83
base0=#839496
base1=#93a1a1
base2=#eee8d5
base3=#fdf6e3
yellow=#b58900
orange=#cb4b16
red=#dc322f
magenta=#d33682
violet=#6c71c4
blue=#268bd2
cyan=#2aa198
green=#859900
std_color=$magenta
wifi_icon=" "
color=$std_color;
eth="$(ip -o address | grep -i "$dev_eth *inet ")"
if [ -n "$eth" ]
then
speed="$(cat /sys/class/net/$dev_eth/speed)"
case $speed in
10) speed="10Base-T" ;;
100) speed="100Base-T" ;;
1000) speed="Gigabit" ;;
*) speed="UNKNOWN $speed" ;;
esac
eth_status="  $speed"
fi
ssid="$(iw dev $dev_wifi link | grep -i SSID)"
if [ -n "$ssid" ]
then
signal="$(awk '/^\s*w/ { print int($3 * 100 / 70) "%" }' /proc/net/wireless)"
wifi_status="$wifi_icon ${signal} ${ssid##*SSID: }"
fi
vpn="$(ip -o address | grep -i "$dev_vpn *inet ")"
if [ -n "$vpn" ]
then
vpn_status=""
color=$green
else
vpn_status=""
fi
echo "${vpn_status} $wifi_status$eth_status"
# vim: ft=sh:expandtab:ts=4:shiftwidth=4

27
.local/bin/status-vol Executable file
View File

@ -0,0 +1,27 @@
#!/bin/sh
# Prints the current volume or  if muted.
vol="$(wpctl get-volume @DEFAULT_AUDIO_SINK@)"
# If muted, print 🔇 and exit.
[ "$vol" != "${vol%\[MUTED\]}" ] && echo  && exit
vol="${vol#Volume: }"
split() {
# For ommiting the . without calling and external program.
IFS=$2
set -- $1
printf '%s' "$@"
}
vol="$(split "$vol" ".")"
vol="${vol##0}"
case 1 in
$((vol >= 70)) ) icon="" ;;
$((vol >= 30)) ) icon="" ;;
$((vol >= 1)) ) icon="" ;;
* ) echo  && exit ;;
esac
echo "$icon $vol%"

7
.local/bin/switchkb Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
##
# Switches to the given keyboard (argument) and sets
##
setxkbmap "$1" -option ctrl:nocaps

28
.local/bin/tmux-sessioniser Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash
# Simple tool that can be run to create a tmux session or
# connect to an existing one.
if [[ $# -eq 1 ]]; then
selected=$1
else
selected=$(find ~/repos ~/.config ~/ ~/work ~/.xmonad ~/nextcloud -mindepth 1 -maxdepth 1 -type d | fzf)
fi
if [[ -z $selected ]]; then
exit 0
fi
selected_name=$(basename "$selected" | tr . _)
tmux_running=$(pgrep tmux)
if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then
tmux new-session -s "$selected_name" -c "$selected"
exit 0
fi
if ! tmux has-session -t="$selected_name" 2> /dev/null; then
tmux new-session -ds "$selected_name" -c "$selected"
fi
tmux switch-client -t "$selected_name"

13
.local/bin/tmux-windowiser Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
branch_name=$(basename $1)
session_name=$(tmux display-message -p "#S")
clean_name=$(echo $branch_name | tr "./" "__")
target="$session_name:$clean_name"
if ! tmux has-session -t $target 2> /dev/null; then
tmux neww -dn $clean_name
fi
shift
tmux send-keys -t $target "$*" Enter

15
.local/bin/tmux-worker Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
session="system"
# Check if the session exists, discarding output
# We can check $? for the exit status (zero for success, non-zero for failure)
tmux has-session -t $session 2>/dev/null
if [ $? != 0 ]; then
# Set up your session
tmux new-session -s "$session"
fi
# Attach to created session
tmux attach-session -t "$session"

11
.local/bin/toggle-sink-mute Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
default_loc=/tmp/pactl-default-sink
if [ ! -f "$default_loc" ] ; then
set-default-sink
fi
default=$(cat $default_loc)
pactl set-sink-mute $default toggle

11
.local/bin/toggle-source-mute Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
default_loc=/tmp/pactl-default-source
if [ ! -f "$default_loc" ] ; then
set-default-source
fi
default=$(cat $default_loc)
pactl set-source-mute $default toggle

13
.local/bin/trayer-toggle Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
test_path="/tmp/trayer.exists"
if [ -f "$test_path" ]; then
echo "stopping trayer"
killall trayer
rm "$test_path"
else
echo "starting trayer"
touch "$test_path"
exec "$@"
fi

35
.local/bin/urlencode Executable file
View File

@ -0,0 +1,35 @@
#!/usr/bin/env bash
# yeah, i totally stole this from stack exchange, no shame
# - rwxrob
# and I stole it from rwxrob
# - inkletblot
rawurlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o
for ((pos = 0; pos < strlen; pos++)); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9]) o="${c}" ;;
*) printf -v o '%%%02x' "'$c'" ;;
esac
encoded+="${o}"
done
echo "${encoded}" # You can either set a return variable (FASTER)
REPLY="${encoded}" #+or echo the result (EASIER)... or both... :p
}
if test -n "$1"; then
rawurlencode "$*"
exit
fi
IFS=
while read -r line; do
rawurlencode "$line"
done

4
.local/bin/vic Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
cmd=$(command -v $1)
test -n "$cmd" && exec vi "$cmd"

9
.local/bin/xmobar-status-bat Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# a simple wrapper for inserting xmobar stuff
status=$(status-bat)
parts=($status)
echo "<fn=1>${parts[0]} </fn> ${parts[1]}"

7
.local/bin/xmobar-status-disk Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
status=$(status-disk)
parts=($status)
echo "<fn=1>${parts[0]} </fn> ${parts[1]}"

View File

@ -0,0 +1,32 @@
#!/bin/sh
stdlayout=us # standard layout takes "default" color
stdname=en-us # arbitrary, descriptive only
base03=#002b36
base02=#073642
base01=#586e75
base00=#657b83
base0=#839496
base1=#93a1a1
base2=#eee8d5
base3=#fdf6e3
yellow=#b58900
orange=#cb4b16
red=#dc322f
magenta=#d33682
violet=#6c71c4
blue=#268bd2
cyan=#2aa198
green=#859900
layout="$(xkb-switch)"
case $layout in
${stdlayout}) color=$green; icon=" "; name=$stdname ;; # f11c fa-keyboard-o
*) color=$magenta; icon=""; name="dvorak" ;; # f11c fa-keyboard-o
esac
echo "<fc=$color><fn=1>$icon </fn> ${name}</fc>"
# vim: ft=sh:expandtab:ts=4:shiftwidth=4

87
.local/bin/xmobar-status-net Executable file
View File

@ -0,0 +1,87 @@
#!/bin/sh
dev_wifi=$(cat "$HOME"/.config/xmobar/dev_wifi)
dev_eth=$(cat "$HOME"/.config/xmobar/dev_eth)
dev_vpn=$(cat "$HOME"/.config/xmobar/dev_vpn)
# base03=#002b36
# base02=#073642
# base01=#586e75
# base00=#657b83
# base0=#839496
# base1=#93a1a1
# base2=#eee8d5
# base3=#fdf6e3
yellow=#b58900
# orange=#cb4b16
red=#dc322f
magenta=#d33682
# violet=#6c71c4
# blue=#268bd2
# cyan=#2aa198
green=#859900
# connectivity status
# states are:
# none (no connectivity)
# portal (behind captive portal)
# limited (connected to network but no internet access)
# full (full internet connectivity)
# unknown
std_color=$magenta
wifi_icon=""
connectivity="$(nmcli networking connectivity)"
case $connectivity in
none) color=$red; icon="" ;; # f056 fa-minus-circle
portal) color=$yellow; icon="" ;; # f05c fa-times-circle-o
limited) color=$yellow; icon="" ;; # f01b fa-arrow-circle-o-up
full) color=$std_color; icon="" ;; # f0aa fa-arrow-circle-up
*) color=$red; icon="" ;; # f29c fa-question-circle-o
esac
eth="$(ip -o address | grep -i "$dev_eth *inet ")"
if [ -n "$eth" ]
then
#eth="${eth##*inet }"
speed="$(cat /sys/class/net/"$dev_eth"/speed)"
case $speed in
10) speed="10Base-T" ;;
100) speed="100Base-T" ;;
1000) speed="Gigabit" ;;
*) speed="UNKNOWN $speed" ;;
esac
#eth_status=" <fn=1></fn> $speed ${eth%%/*}"
eth_status=" <fn=1> </fn> $speed"
fi
ssid="$(iw dev "$dev_wifi" link | grep -i SSID)"
if [ -n "$ssid" ]
then
signal="$(iw dev "$dev_wifi" station dump | grep -E '[^ ]signal avg')"
signal="${signal#*-}"
signal="${signal%% *}"
signal="$((2*(100-signal)))"
signal=$((signal/5*5)) # get rid of some jitter
((signal > 100)) && signal=100
wifi_status=" <fn=1>$wifi_icon </fn> <fc=$green>${signal}</fc>% ${ssid##*SSID: }"
fi
vpn="$(ip -o address | grep -i "$dev_vpn *inet ")"
if [ -n "$vpn" ]
then
#vpn="${vpn##*inet }"
#vpn_status="<fn=1></fn> ${vpn%%/*}"
vpn_status="<fn=1> </fn> "
color=$green
else
vpn_status="<fn=1> </fn> "
fi
echo "<fc=$color>${vpn_status}<fn=1>$icon </fn>${connectivity##*full}$wifi_status$eth_status</fc>"
# vim: ft=sh:expandtab:ts=4:shiftwidth=4

View File

@ -0,0 +1,6 @@
#!/bin/sh
count=$(yay -Qu | wc -l)
icon=""
echo "<fn=1>$icon </fn> $count"

46
.local/bin/xmobar-status-vol Executable file
View File

@ -0,0 +1,46 @@
#!/bin/sh
base03=#002b36
base02=#073642
base01=#586e75
base00=#657b83
base0=#839496
base1=#93a1a1
base2=#eee8d5
base3=#fdf6e3
yellow=#b58900
orange=#cb4b16
red=#dc322f
magenta=#d33682
violet=#6c71c4
blue=#268bd2
cyan=#2aa198
green=#859900
color=$cyan
vol="$(pamixer --get-volume)"
if [ $(pamixer --get-mute) = true ]; then
vol=MUTE
color=$red
icon="" # fa-volume-off f026
echo "<fc=$color><fn=1>$icon </fn>$vol</fc>"
fi
if [ "$vol" -gt "100" ]; then
icon=""
color=$orange
elif [ "$vol" -gt "70" ]; then
icon=""
elif [ "$vol" -gt "30" ]; then
icon=""
elif [ "$vol" -gt "0" ]; then
icon=""
else
vol=MUTE
color=$red
icon=""
fi
echo "<fc=$color><fn=1>$icon </fn>$vol</fc>"

View File

@ -0,0 +1,36 @@
#!/bin/sh
# simple script to read weather from airport code and return it for display in xmobar
weather=$(weather-report $1 -m --no-cache -n | \
grep -e '\[' \
-e 'Temp' | \
sed -e 's/\[using result //' \
-e 's/,.*\]//' \
-e 's/Temp.*: //' \
-e 's/ C//' \
-e 's/ //')
readarray -t y <<< "$weather"
loc=${y[0]}
temp=${y[1]}
color=""
if ((temp > 30))
then
color="#ff5555"
elif ((temp > 25))
then
color="#ffb86c"
elif ((temp > 20))
then
color="#f1fa8c"
elif ((temp > 10))
then
color="#50fa7b"
else
color="#8be9fd"
fi
echo -e "<fc=#ff79c6>$loc <fc=$color>$temp</fc>C</fc>"

View File

@ -0,0 +1,48 @@
#!/bin/sh
# Copied from https://github.com/jaor/xmobar/issues/239#issuecomment-233206552
# Detects the width of running trayer-srg window (xprop name 'panel')
# and creates an XPM icon of that width, 1px height, and transparent.
# Outputs an <icon>-tag for use in xmobar to display the generated
# XPM icon.
#
# Run script from xmobar:
# `Run Com "/where/ever/trayer-padding-icon.sh" [] "trayerpad" 10`
# and use `%trayerpad%` in your template.
# Function to create a transparent Wx1 px XPM icon
create_xpm_icon () {
timestamp=$(date)
pixels=$(for i in `seq $1`; do echo -n "."; done)
cat << EOF > "$2"
/* XPM *
static char * trayer_pad_xpm[] = {
/* This XPM icon is used for padding in xmobar to */
/* leave room for trayer-srg. It is dynamically */
/* updated by by trayer-padding-icon.sh which is run */
/* by xmobar. */
/* Created: ${timestamp} */
/* <w/cols> <h/rows> <colors> <chars per pixel> */
"$1 1 1 1",
/* Colors (none: transparent) */
". c none",
/* Pixels */
"$pixels"
};
EOF
}
# Width of the trayer window
width=$(xprop -name panel | grep 'program specified minimum size' | cut -d ' ' -f 5)
# Icon file name
iconfile="/tmp/trayer-padding-${width}px.xpm"
# If the desired icon does not exist create it
if [ ! -f $iconfile ]; then
create_xpm_icon $width $iconfile
fi
# Output the icon tag for xmobar
echo "<icon=${iconfile}/>"

12
.local/bin/xmonad-keys-help Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
sed -n '/--START_KEYS/,/--END_KEYS/p' $HOME/.xmonad/xmonad.hs | \
grep -e ', ("' \
-e '\[ ("' \
-e '--NOTE' | \
grep -v '\-\- , ' |\
sed -e 's/^[ \t,]*//' \
-e 's/\[ (/(/' \
-e 's/--NOTE /\n/' \
-e 's/, / --> /' | \
yad --text-info --back=#121e32 --fore=#dfdfef --geometry=1200x800

40
.local/bin/zk-gen-dir-md-toc Executable file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Generates a simple index of the current folder and subfolders creating
# a table of contents of all of the .md files contained within.
long_contents=$(find . | sort)
# replace the absolute path in all lines
contents=${long_contents//\.\//}
prev=""
echo "$contents" | while read -r line || [[ -n $line ]];
do
# the line contains a file, or hidden dir
if [[ $line == *"."* ]]; then
# if the line is an md, not in the .zk dir, and not a dir
if [[ $line == *".md"* && ! -d "$line" && $line != *".zk"* ]]; then
# extract the name of the file
name=$(echo "$line" | sed 's/.*\///' | sed 's/\.md//')
# create the link
name="[$name]($line)"
else
continue
fi
else
# the folders are titles
name=$(echo -e "\n## $line")
fi
# two titles in a row means empty folder.
if [[ $prev == *"##"* && $name == *"##"* ]]; then
prev=$name
name=$(echo -e "Folder Empty.\n$prev")
else
prev=$name
fi
echo "$name"
done

14
.local/bin/zk-gen-index Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
toc=$(zk-gen-dir-md-toc)
echo -e "START_TOC$toc\nEND_TOC" > temp.md
if [ -f index.md ]; then
perl -i -p0e 's/START_TOC.*END_TOC/`cat temp.md`/se' index.md
# sed -i "s/START_TOC.*END_TOC/\${toc}/g" index.md
else
echo -e "# Index\n\nSTART_TOC\n$toc\nEND_TOC" > index.md
fi
rm temp.md

3
scripts/ganttproject Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
/opt/ganttproject/ganttproject

33
scripts/update-dmenu-bluetooth Executable file
View File

@ -0,0 +1,33 @@
#!/bin/sh
echo "This script will clone Layerex's dmenu-bluetooth and copy the scripts from it that I use."
read -r -p "Are you sure you want to continue [Y/n]" input
case $input in
[nN][oO] | [nN])
echo "Okay, exiting..."
exit 0
;;
*)
printf "\n"
;;
esac
git clone https://github.com/Layerex/dmenu-bluetooth
printf "\nCopying scripts...\n"
dir="dmenu-bluetooth"
if [ -d "$dir" ]; then
for script in "$HOME"/.local/bin/*; do
if [ -f "./$dir/${script##*/}" ]; then
cp "./$dir/${script##*/}" "$HOME/.local/bin/" -v
fi
done
printf "\nRemoving %s..." "$dir"
rm -rf "./$dir" && printf "\nUpdate Complete."
else
echo "directory $dir does not exist"
fi

View File

@ -0,0 +1,33 @@
#!/bin/bash
echo "This script will clone firecat53's networkmanager-dmenu and copy the scripts from it that I use."
read -r -p "Are you sure you want to continue [Y/n]" input
case $input in
[nN][oO] | [nN])
echo "Okay, exiting..."
exit 0
;;
*)
printf "\n"
;;
esac
git clone https://github.com/firecat53/networkmanager-dmenu
printf "\nCopying scripts...\n"
dir="networkmanager-dmenu"
if [ -d "$dir" ]; then
for script in "$HOME"/.local/bin/*; do
if [ -f "./$dir/${script##*/}" ]; then
cp "./$dir/${script##*/}" "$HOME/.local/bin/" -v
fi
done
printf "\nRemoving %s..." "$dir"
rm -rf "./$dir" && printf "\nUpdate Complete."
else
echo "directory $dir does not exist"
fi

40
scripts/update-voidrice Executable file
View File

@ -0,0 +1,40 @@
#!/bin/bash
# I use some of Luke Smiths scripts and although they don't often update, they are known to.
# The output is going to include a bunch of copy errors as I'm just copying each file in this directory from his .local/bin back to here, obviously my custom scripts won't exist so they'll fail.
echo "This script will clone Luke Smith's voidrice and copy the scripts from it that I use."
read -r -p "Are you sure you want to continue [Y/n]" input
case $input in
[nN][oO] | [nN])
echo "Okay, exiting..."
exit 0
;;
*)
printf "\n"
;;
esac
git clone https://github.com/LukeSmithxyz/voidrice
printf "\nCopying scripts...\n"
if [ -d voidrice ]; then
for script in "$HOME"/.local/bin/*; do
if [ -f "./voidrice/.local/bin/${script##*/}" ]; then
cp "./voidrice/.local/bin/${script##*/}" "$HOME/.local/bin/" -v
fi
done
for cron in "$HOME"/.local/bin/cron/*; do
if [ -f "./voidrice/.local/bin/cron/${cron##*/}" ]; then
cp "./voidrice/.local/bin/cron/${cron##*/}" "$HOME/.local/bin/cron/" -v
fi
done
printf "\nRemoving voidrice..."
rm -rf ./voidrice && printf "\nUpdate Complete."
else
echo "directory voidrice does not exist"
fi

3
scripts/zotero Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
/opt/zotero/zotero