Update multiple scripts

This commit is contained in:
ItsDrike 2022-10-29 23:31:40 +02:00
parent 542ac098b4
commit 44b010a541
No known key found for this signature in database
GPG key ID: B014E761034AF742
17 changed files with 424 additions and 37 deletions

View file

@ -0,0 +1,85 @@
#!/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.
command -v setbg >/dev/null && setbg # Fix background if screen size/arangement has changed.
}
# 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
if [ "$(echo "$screens" | wc -l)" -lt 2 ]; then
onescreen "$screens"
notify-send "💻 Only one screen detected." "Using it in its optimal settings..."
else
# 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
fi
postrun

View file

@ -0,0 +1,11 @@
#!/bin/sh
# Dmenu prompt to easily search for a man page to open
page="$(apropos --long "$1" | dmenu -i -l 10 | awk '{print $2, $1}' | tr -d '()')"
if tty -s; then
man $page
else
$TERMINAL -e man $page
fi

View file

@ -0,0 +1,68 @@
#!/bin/sh
# Gives 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
# existing 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."
}
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"
simple-mtpfs --device "$chosen" "$mp"
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."
}
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,mountpoint" | grep 'part\|rom' | awk '$4==""{printf "%s (%s)\n",$1,$3}')"
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

View file

@ -0,0 +1,121 @@
#!/bin/sh
# Usage:
# `$0`: Ask for recording type via dmenu
# `$0 screencast`: Record both audio and screen
# `$0 video`: Record only screen
# `$0 audio`: Record only audio
# `$0 kill`: Kill existing recording
#
# If there is already a running instance, user will be prompted to end it.
screencast() {
ffmpeg -y \
-f x11grab \
-framerate 60 \
-s "$(xdpyinfo | grep dimensions | awk '{print $2;}')" \
-i "$DISPLAY" \
-f alsa -i default \
-r 30 \
-c:v h264 -crf 0 -preset ultrafast -c:a aac \
"$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mp4" &
echo $! > /tmp/recordingpid
}
video() {
ffmpeg \
-f x11grab \
-s "$(xdpyinfo | grep dimensions | awk '{print $2;}')" \
-i "$DISPLAY" \
-c:v libx264 -qp 0 -r 30 \
"$HOME/video-$(date '+%y%m%d-%H%M-%S').mkv" &
echo $! > /tmp/recordingpid
}
webcamhidef() {
ffmpeg \
-f v4l2 \
-i /dev/video0 \
-video_size 1920x1080 \
"$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
echo $! > /tmp/recordingpid
}
webcam() {
ffmpeg \
-f v4l2 \
-i /dev/video0 \
-video_size 640x480 \
"$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
echo $! > /tmp/recordingpid
}
audio() {
ffmpeg \
-f alsa -i default \
"$HOME/audio-$(date '+%y%m%d-%H%M-%S').mp3" &
echo $! > /tmp/recordingpid
}
asktoend() {
response=$(printf "No\\nYes" | dmenu -i -p "Recording still active. End recording?") &&
[ "$response" = "Yes" ] && killrecording
}
videoselected()
{
slop -f "%x %y %w %h" > /tmp/slop
read -r X Y W H < /tmp/slop
rm /tmp/slop
ffmpeg \
-f x11grab \
-framerate 60 \
-video_size "$W"x"$H" \
-i :0.0+"$X,$Y" \
-c:v libx264 -qp 0 -r 30 \
"$HOME/box-$(date '+%y%m%d-%H%M-%S').mkv" &
echo $! > /tmp/recordingpid
}
killrecording() {
recpid="$(cat /tmp/recordingpid)"
# kill with SIGTERM, allowing finishing touches.
kill -15 "$recpid" 2>/dev/null
rm -f /tmp/recordingpid
# even after SIGTERM, ffmpeg may still run, so SIGKILL it.
sleep 3
kill -9 "$recpid" 2>/dev/null
exit
}
askrecording() { \
choice=$(printf "screencast\\nvideo\\nvideo selected\\naudio\\nwebcam\\nwebcam (hi-def)" | dmenu -i -p "Select recording style:")
case "$choice" in
screencast) screencast;;
audio) audio;;
video) video;;
*selected) videoselected;;
webcam) webcam;;
"webcam (hi-def)") webcamhidef;;
esac
}
case "$1" in
screencast) screencast;;
audio) audio;;
video) video;;
*selected) videoselected;;
kill) killrecording;;
*)
if [ -f /tmp/recordingpid ]; then
recpid="$(cat /tmp/recordingpid)"
if ps -p $recpid > /dev/null; then
asktoend
exit
fi
fi
askrecording;;
esac

View file

@ -0,0 +1,44 @@
#!/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.
unmountusb() {
[ -z "$drives" ] && exit
chosen="$(echo "$drives" | dmenu -i -p "Unmount which drive?")" || exit 1
chosen="$(echo "$chosen" | awk '{print $1}')"
[ -z "$chosen" ] && exit
sudo -A umount "$chosen" && notify-send "💻 USB unmounting" "$chosen unmounted."
}
unmountandroid() { \
chosen="$(awk '/simple-mtpfs/ {print $2}' /etc/mtab | dmenu -i -p "Unmount which device?")" || exit 1
[ -z "$chosen" ] && exit
sudo -A umount -l "$chosen" && notify-send "🤖 Android unmounting" "$chosen unmounted."
}
asktype() { \
choice="$(printf "USB\\nAndroid" | dmenu -i -p "Unmount a USB drive or Android device?")" || exit 1
case "$choice" in
USB) unmountusb ;;
Android) unmountandroid ;;
esac
}
drives=$(lsblk -nrpo "name,type,size,mountpoint" | awk '$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "%s (%s)\n",$4,$3}')
if ! grep simple-mtpfs /etc/mtab; then
[ -z "$drives" ] && echo "No drives to unmount." && exit
echo "Unmountable USB drive detected."
unmountusb
else
if [ -z "$drives" ]
then
echo "Unmountable Android device detected."
unmountandroid
else
echo "Unmountable USB drive(s) and Android device(s) detected."
asktype
fi
fi

View file

@ -0,0 +1,8 @@
#!/bin/sh
# Simple wrapper to quickly execute a simple python command and have it printed out.
# The output is then sent back via notification.
#
# This already has random imported, since that's a common use-case for this script.
echo "" | dmenu -i -p "Python" | xargs -0 -I % python -c "import random;print(%)" | xargs -I % notify-send "Pyrun output:" "%"

View file

@ -0,0 +1,36 @@
#!/bin/sh
# This is inspired by dmenu's todo script made by suckless
#
# Manage TODO tasks in dmenu by writing them, remove by selecting
# an existing entry
#
# Configurable variables
# ---------------------------------------------------------------------
FILE="${XDG_DATA_HOME:-$HOME/.local/share}/todos"
PROMPT="Add/delete a task: "
# Logic
# ---------------------------------------------------------------------
mkdir -p "$(dirname $FILE)"
touch "$FILE"
height=$(wc -l "$FILE" | awk '{print $1}')
# Run dmenu and keep restarting it until it returns an empty output
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
while [ -n "$cmd" ]; do
# If the output matched an existing TODO, remove it
if grep -q "^$cmd\$" "$FILE"; then
grep -v "^$cmd\$" "$FILE" > "$FILE.$$"
mv "$FILE.$$" "$FILE"
height=$(( height - 1 ))
# If the output didn't match an existing TODO, it's a new one, add it
else
echo "$cmd" >> "$FILE"
height=$(( height + 1 ))
fi
# Keep restarting until empty output
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
done

View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
import feedparser
import subprocess
URL = "https://itsdrike.com/posts/index.xml"
def main():
feed = feedparser.parse(URL)
titles = {entry['title']: entry['link'] for entry in feed['entries']}
selected_page = subprocess.check_output(
["dmenu", "-i", "-p", "Post"],
input="\n".join(titles.keys()), universal_newlines=True
)
link = titles[selected_page.strip()]
subprocess.check_output(["xsel", "-bi"], input=link, universal_newlines=True)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,11 @@
#!/bin/sh
# Menu prompt to easily search for a man page to open
page="$(apropos --long "$1" | menuprompt --choose -p "Pick man page" | awk '{print $2, $1}' | tr -d '()')"
if tty -s; then
man "$page"
else
$TERMINAL -e man "$page"
fi

View file

@ -0,0 +1,20 @@
#!/bin/sh
# This script is the a wrapper around menuprompt automatically passing options
# to prompt for a password. This is necessary for SUDO_ASKPASS variable, as it
# expects a path to a single executable.
#
# SUDO_ASKPASS is an environment variable that can be set to a path of an executable,
# which will run if sudo can't obtain password from terminal (stdin), i.e. running in
# a graphical application. This script will use menuprompt to obtain the plaintext
# password and which sudo will then read and verify.
#
# This is as safe as any other SUDO_ASKPASS program, and it needs to return the final
# password in plaintext for sudo to read it, however pipes retain the same filesystem user
# permissions and shouldn't be accessible to other non-priviledged users, so it's not
# a huge security issue, but know that it will deal with plaintext passwords.
#
# You can test whether this worked by running sudo -A echo "test", making sudo use the
# SUDO_ASKPASS program to obtain the password.
menuprompt -P -p "$1" 2>/dev/null

View file

@ -0,0 +1,338 @@
#!/bin/bash
# Compatibility executable script for applications running dmenu, which
# runs wofi in dmenu mode instead. Note: In many cases the arguments won't
# be compatible and this will fail, however the primary way scripts use
# dmenu is just for simple prompts, using the `-p` flag. and wofi does fully
# support this usage.
# Wrapper script for menu application independent prompts. This script will pick
# the appropriate installed prompt tool (dmenu/rofi/wofi) and use run it accordingly
# to picked usage option(s).
help() {
cat << EOF
usage: menuprompt
Menuprompt is a wrapper script for menu application independent prompt (dmenu/rofi/wofi)
options:
-h, --help show this help message and exit
-p, --prompt [MESSAGE] Prompt to be displayed in the menu
-a, --app [MENU_APP] Overrides the app picked by default, must be one of supported apps
-v, --verbose Show debug information (printed to stderr to retain pipeable stdout output)
-vv, --very-verbose Show even more debug information
--dry-run Don't actually run the final menu command (only useful with --verbose)
actions:
-c, --choose
Basic pick prompt, picking from piped newline separated options
The picked option will be printed to stdout.
usage: printf 'No\nYes' | menuprompt -c -p 'Pick one: '
-yn, --yesno
Essentially an alias for printf 'No\nYes' | menuprompt -p "\$1", however instead of printing the result
to stdout, this instead exits with code 0 on success, 1 on failure, or 2 on getting other input. This
allows for easily combining it with other applications through chaining.
example: menuprompt --yesno && poweroff or menuprompt -yn -p "Do you really want to quit?" && killall Hyprland
-P, --password
Password prompt - hides the input characters
example: menuprompt -P -p "Enter password:" | xargs -I {} echo "Hah, got your password: {}"
supported menu applications and their actions:
dmenu:
- choose: supported
- yesno: supported
- password: supported, but requires password patch for -P argument
wofi:
- choose: supported
- yesno: supported
- password: supported, however setting --prompt won't have any effect
rofi:
- choose: supported
- yesno: supported
- password: not supported - no option for hidden input
passing custom arguments to menu app:
Note that when passing extra arguments, you should always explicity specify the menu application to use
as it's not guaranteed that all supported args will support given extra argument(s), nor that if they do
their support is consistent and the same argument does the same thing.
examples:
Make dmenu run on the bottom of the screen:
printf "A\nB\nC" menuprompt --app dmenu --choose -- -b
Run wofi with 20% width on the yesno prompt:
menuprompt --yesno -p "Do you really want to quit?" --app wofi -- --width=20%
exit codes:
0: Menu app ran successfully, action completed
1-2: Action specific
3: User error - invalid usage
4: Unable to pick menu app (no usable menu app installed?)
5: Internal menuprompt error, report this!
EOF
}
pick_menu_app() {
if [ -n "$MENU_APP" ]; then
if command -v "$MENU_APP" >/dev/null; then
echo "$MENU_APP"
return 0
else
>&2 echo "Specified menu app by (set by \$MENU_APP) isn't a valid command \`$MENU_APP\`"
exit 3
fi
fi
if [ "$XDG_SESSION_TYPE" = "x11" ]; then
if command -v dmenu >/dev/null; then
echo "dmenu"
elif command -v rofi >/dev/null; then
echo "rofi"
else
>&2 echo "Menu application for x11 not found, neither dmenu nor rofi available."
exit 3
fi
elif [ "$XDG_SESSION_TYPE" = "wayland" ]; then
if command -v wofi >/dev/null; then
echo "wofi"
else
>&2 echo "Menu application for wayland not found, wofi not available."
exit 3
fi
else
>&2 echo "Unknown session type, expected \$XDG_SESSION_TYPE to be \`wayland\` or \`x11\`, got \`$XDG_SESSION_TYPE\`."
exit 3
fi
}
# Runs picked menu application with
# $1 pipe STDIN if set to "1", runs normally if "0"
# $2 app to use (result of pick_menu_app)
# $3 prompt to be shown (can be empty)
# $4- extra args to pass to given menu app
runmenu() {
pipe_stdin="$1"
app="$2"
prompt="$3"
shift 3
if [ "$VERBOSITY" -ge 2 ]; then
if [ $# -gt 0 ]; then
>&2 echo "Making menu command for app: \`$app\`, with prompt: \`$prompt\` and extra args: \`$*\`"
else
>&2 echo "Making menu command for app: \`$app\`, with prompt: \`$prompt\` (no extra args)"
fi
fi
if [ "$app" = "dmenu" ] && [ "$pipe_stdin" -eq 0 ]; then
>&2 echo "Dmenu can only be used with piped STDIN (forgot --choose?)"
exit 3
fi
args=()
case "$app" in
dmenu)
args+=("-i")
;;
wofi)
args+=("--dmenu")
;;
rofi)
args+=("-dmenu")
;;
*)
>&2 echo "Unsupported menu app: $app"
exit 4
;;
esac
if [ -n "$prompt" ]; then
if [ "$app" = "wofi" ] && [ "$pipe_stdin" -eq 0 ]; then
>&2 echo "WARNING: wofi prompt can only be displayed with piped STDIN (forgot --choose?)"
fi
args+=("-p" "$prompt")
fi
if [ "$VERBOSITY" -ge 1 ]; then
>&2 echo "Generated command: \`$app ${args[*]} $*\`"
fi
if [ "$DRY_RUN" -ne 1 ]; then
if [ "$pipe_stdin" -eq 1 ]; then
cat | $app "${args[@]}" "$@"
elif [ "$pipe_stdin" -eq 0 ]; then
$app "${args[@]}" "$@"
return $?
else
>&2 echo "Internal error, pipe_stdin set to invalid value: \`$pipe_stdin\`"
exit 5
fi
else
return 0
fi
}
# $1 app to use (result of pick_menu_app)
# $2 prompt to be shown (can be empty)
# $2- extra arguments to pass to given menu app
password() {
app="$1"
prompt="$2"
shift 2
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "Prompting for password..."
fi
if [ "$app" = "dmenu" ]; then
if ! runmenu "0" "$app" "$prompt" -P "$@"; then
>&2 echo "To use dmenu for passwords, you need to apply the password patch."
exit 3
fi
elif [ "$app" = "wofi" ]; then
runmenu "0" "$app" "$prompt" -P "$@"
elif [ "$app" = "rofi" ]; then
>&2 echo "Rofi doesn't support password/hidden input."
exit 3
else
>&2 echo "Unsupported menu application picked: \`$app\`"
exit 4
fi
}
# $1 app to use (result of pick_menu_app)
# $2 prompt to be shown (can be empty)
# $3- extra arguments to pass to the menu app
yesno() {
app="$1"
prompt="$2"
shift 2
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "Prompting for yesno..."
fi
temp="$(printf "No\nYes" | runmenu "1" "$app" "$prompt" "$@")"
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "Yesno prompt answer: \`$temp\`"
fi
if [ "$temp" = "Yes" ]; then
return 0
elif [ "$temp" = "No" ]; then
return 1
else
return 2
fi
}
#@ - extra args to pass to picked menu app
run() {
app="$(pick_menu_app)"
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "ACTION: \`$ACTION\`"
>&2 echo "PROMPT_ARG: \`$PROMPT_ARG\`"
>&2 echo "picekd app: \`$app\`"
>&2 echo "Extra args: \`$*\`"
fi
if [ -z "$ACTION" ]; then
runmenu "0" "$app" "$PROMPT_ARG" "$@"
return $?
elif [ "$ACTION" = "choose" ]; then
runmenu "1" "$app" "$PROMPT_ARG" "$@"
return $?
elif [ "$ACTION" = "yesno" ]; then
yesno "$app" "$PROMPT_ARG" "$@"
return $?
elif [ "$ACTION" = "password" ]; then
password "$app" "$PROMPT_ARG" "$@"
return $?
fi
>&2 echo "Huh? How did you get here?"
>&2 echo "Rerun this command with the --verbose flag and submit a report with the captured output."
exit 5
}
ACTION=""
PROMPT_ARG=""
MENU_APP="${MENU_APP-""}"
VERBOSITY=0
DRY_RUN=0
while [ "$1" ]; do
case "$1" in
# options
-h|--help)
help
exit 0
;;
-a|--app)
MENU_APP="$2"
shift 2
;;
-p|--prompt)
PROMPT_ARG="$2"
shift 2
;;
-v|--verbose)
VERBOSITY=1
shift
;;
-vv|--very-verbose)
VERBOSITY=2
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
# actions
-c|--choose)
if [ -n "$ACTION" ]; then
>&2 echo "Action already set, can't specify multiple actions"
exit 3
fi
ACTION="choose"
shift
;;
-yn|--yesno)
if [ -n "$ACTION" ]; then
>&2 echo "Action already set, can't specify multiple actions"
exit 3
fi
ACTION="yesno"
shift
;;
-P|--password)
if [ -n "$ACTION" ]; then
>&2 echo "Action already set, can't specify multiple actions"
exit 3
fi
ACTION="password"
shift
;;
--)
shift
run "$@"
exit $?
;;
# err
*)
>&2 echo "Unrecognized argument: $1"
>&2 echo "use -h or --help for help"
exit 4
;;
esac
done
run

View file

@ -0,0 +1,36 @@
#!/bin/sh
# This is inspired by dmenu's todo script made by suckless
#
# Manage TODO tasks in dmenu by writing them, remove by selecting
# an existing entry
#
# Configurable variables
# ---------------------------------------------------------------------
FILE="${XDG_DATA_HOME:-$HOME/.local/share}/todos"
PROMPT="Add/delete a task: "
# Logic
# ---------------------------------------------------------------------
mkdir -p "$(dirname $FILE)"
touch "$FILE"
height=$(wc -l "$FILE" | awk '{print $1}')
# Run dmenu and keep restarting it until it returns an empty output
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
while [ -n "$cmd" ]; do
# If the output matched an existing TODO, remove it
if grep -q "^$cmd\$" "$FILE"; then
grep -v "^$cmd\$" "$FILE" > "$FILE.$$"
mv "$FILE.$$" "$FILE"
height=$(( height - 1 ))
# If the output didn't match an existing TODO, it's a new one, add it
else
echo "$cmd" >> "$FILE"
height=$(( height + 1 ))
fi
# Keep restarting until empty output
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
done