diff options
-rwxr-xr-x | bin/dim-screen.sh | 51 | ||||
-rwxr-xr-x | bin/emacs-pkg-install | 45 | ||||
-rwxr-xr-x | bin/i3lock-wrapper | 5 | ||||
-rwxr-xr-x | bin/mutt-displayfilter-gpg | 52 | ||||
-rwxr-xr-x | bin/mutt-fetchbug | 161 | ||||
-rwxr-xr-x | bin/mutt-open | 76 | ||||
-rwxr-xr-x | bin/mutt-remember-mail | 23 | ||||
-rwxr-xr-x | bin/pycombine | 240 | ||||
-rwxr-xr-x | bin/touchpad-enable-tap | 7 | ||||
-rwxr-xr-x | bin/xrandr-rul | 21 |
10 files changed, 681 insertions, 0 deletions
diff --git a/bin/dim-screen.sh b/bin/dim-screen.sh new file mode 100755 index 0000000..949ed85 --- /dev/null +++ b/bin/dim-screen.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# Debian package: xbacklight + +# Brightness will be lowered to this value. +min_brightness=0 + +# If your video driver works with xbacklight, set -time and -steps for fading +# to $min_brightness here. Setting steps to 1 disables fading. +fade_time=2000 +fade_steps=200 + +# Time to sleep (in seconds) between increments when using sysfs. If unset or +# empty, fading is disabled. +fade_step_time=0.05 + + +get_brightness() { + if [[ -z $sysfs_path ]]; then + xbacklight -get + else + cat $sysfs_path + fi +} + +set_brightness() { + if [[ -z $sysfs_path ]]; then + xbacklight -steps 1 -set $1 + else + echo $1 > $sysfs_path + fi +} + +fade_brightness() { + if [[ -z $sysfs_path ]]; then + xbacklight -time $fade_time -steps $fade_steps -set $1 + elif [[ -z $fade_step_time ]]; then + set_brightness $1 + else + local level + for level in $(eval echo {$(get_brightness)..$1}); do + set_brightness $level + sleep $fade_step_time + done + fi +} + +trap 'exit 0' TERM INT +trap "set_brightness $(get_brightness); kill %%" EXIT +fade_brightness $min_brightness +sleep 2147483647 & +wait diff --git a/bin/emacs-pkg-install b/bin/emacs-pkg-install new file mode 100755 index 0000000..93b747f --- /dev/null +++ b/bin/emacs-pkg-install @@ -0,0 +1,45 @@ +#!/bin/bash +# Gotten from: https://gist.github.com/padawanphysicist/d6299870de4ef8ad892f +# +# I wrapped the code constructed in +# +# http://hacks-galore.org/aleix/blog/archives/2013/01/08/install-emacs-packages-from-command-line +# +# in a single bash script, so I would a single code snippet. +# + + +# Elisp script is created as a temporary file, to be removed after installing +# the package +elisp_script_name=$(mktemp /tmp/emacs-pkg-install-el.XXXXXX) +elisp_code=" +;; +;; Install package from command line. Example: +;; +;; $ emacs --batch --expr \"(define pkg-to-install 'smex)\" -l emacs-pkg-install.el +;; +(require 'package) +(package-initialize) +(add-to-list 'package-archives + '(\"melpa\" . \"http://melpa.milkbox.net/packages/\") t) +;;(add-to-list 'package-archives +;; '(\"marmalade\" . \"http://marmalade-repo.org/packages/\") t) +;; Fix HTTP1/1.1 problems +(setq url-http-attempt-keepalives nil) +(package-refresh-contents) +(package-install pkg-to-install)" + +echo "$elisp_code" > $elisp_script_name + +if [ $# -lt 1 ] +then + echo "Usage: `basename $0` <package 1> <package 2> ..." + exit 1 +fi + +for pkg_name in $@; do + emacs --batch --eval "(defconst pkg-to-install '$pkg_name)" -l $elisp_script_name +done + +# Remove tmp file +rm "$elisp_script_name" diff --git a/bin/i3lock-wrapper b/bin/i3lock-wrapper new file mode 100755 index 0000000..7c8d8e2 --- /dev/null +++ b/bin/i3lock-wrapper @@ -0,0 +1,5 @@ +#!/bin/bash + +killall -SIGUSR1 dunst +i3lock --nofork -c "#000000" -e -f +killall -SIGUSR2 dunst diff --git a/bin/mutt-displayfilter-gpg b/bin/mutt-displayfilter-gpg new file mode 100755 index 0000000..ccd99d1 --- /dev/null +++ b/bin/mutt-displayfilter-gpg @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Copyright 2017 Sebastian Schmittner + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +""" +import re +import sys + + +def shorten_tofu_blocks(input): + pattern = r"(?s)gpg: Verified ([0-9]+) .*(ago|as being bad)\." + replace = r"gpg: TOFU \1" + return re.sub(pattern, replace, input) + + +def remove_aka_list(input): + pattern = r"gpg:[ \t]+aka [^\n]*\n" + replace = "" + return re.sub(pattern, replace, input) + + +def shorten_untrusted_warning(input): + pattern = r"(?s)gpg: WARNING: This key is not certified with sufficiently trusted signatures!.*key fingerprint: [^\n]+" + replace = "gpg: WARNING: untrusted signature" + return re.sub(pattern, replace, input) + + +def main(): + input = sys.stdin.read() + input = shorten_tofu_blocks(input) + input = remove_aka_list(input) + input = shorten_untrusted_warning(input) + sys.stdout.write(input) + + +# goto main +if __name__ == "__main__": + main() diff --git a/bin/mutt-fetchbug b/bin/mutt-fetchbug new file mode 100755 index 0000000..93ffc58 --- /dev/null +++ b/bin/mutt-fetchbug @@ -0,0 +1,161 @@ +#!/usr/bin/perl -w +# +# mutt-fetchbug, extensively based off of +# mutt-notmuch - notmuch (of a) helper for Mutt +# +# Copyright: © 2011 Stefano Zacchiroli <zack@upsilon.cc> +# License: GNU General Public License (GPL), version 3 or above +# +# Differences between mutt-notmuch and mutt-fetchbug are +# Copyright: © 2012 Ryan Kavanagh <rak@debian.org> +# License: GNU General Public License (GPL), version 3 or above +# +# See the bottom of this file for more documentation. +# A manpage can be obtained by running "pod2man mutt-fetchbug > mutt-fetchbug.1" + +use strict; +use warnings; + +use File::Path; +use Getopt::Long; +use Pod::Usage; + +# search($btsmbox, $query) +# Fetch bugs matching $query with bts; store results in $btsmbox +sub search($$) { + my ($btsmbox, $query) = @_; + + system("bts --cache-mode=mbox cache $query" + . " && ln -fs ~/.cache/devscripts/bts/$query.mbox $btsmbox"); +} + +sub search_action($$@) { + my ($interactive, $btsmbox, @params) = @_; + + if (! $interactive) { + fetch($btsmbox, join(' ', @params)); + } else { + my $query = ""; + my $done = 0; + while (! $done) { + print "bug number ('?' for man): "; + chomp($query = <STDIN>); + if ($query eq "?") { + system("man bts"); + } elsif ($query eq "") { + $done = 1; # quit doing nothing + } else { + search($btsmbox, $query); + $done = 1; + } + } + } +} + +sub die_usage() { + my %podflags = ( "verbose" => 1, + "exitval" => 2 ); + pod2usage(%podflags); +} + +sub main() { + my $btsmbox = "$ENV{HOME}/.cache/mutt_btsresults"; + my $interactive = 0; + my $help_needed = 0; + + my $getopt = GetOptions( + "h|help" => \$help_needed, + "o|output-mbox=s" => \$btsmbox, + "p|prompt" => \$interactive); + if (! $getopt || $#ARGV < 0) { die_usage() }; + my ($action, @params) = ($ARGV[0], @ARGV[1..$#ARGV]); + + if ($help_needed) { + die_usage(); + } elsif ($action eq "search" && $#ARGV == 0 && ! $interactive) { + print STDERR "Error: no search term provided\n\n"; + die_usage(); + } elsif ($action eq "search") { + search_action($interactive, $btsmbox, @params); + } else { + die_usage(); + } +} + +main(); + +__END__ + +=head1 NAME + +mutt-fetchbug - 'bts show' frontend for Mutt + +=head1 SYNOPSIS + +=over + +=item B<mutt-fetchbug> [I<OPTION>]... search [I<SEARCH-TERM>]... + +=back + +=head1 DESCRIPTION + +mutt-fetchbug is a frontend to the 'bts show' command (Debian package: +devscripts) designed to fetch bugs and place them in a predefined mbox. The +search term should typically be a bug number. + +=head1 OPTIONS + +=over 4 + +=item -o DIR + +=item --output-mbox DIR + +Store search results as (symlink) mbox MBOX. Beware: MBOX will be overwritten. +(Default: F<~/.cache/mutt_btsresults/>) + +=item -p + +=item --prompt + +Instead of using command line search terms, prompt the user for them (only for +"search"). + +=item -h + +=item --help + +Show usage information and exit. + +=back + +=head1 INTEGRATION WITH MUTT + +mutt-fetchbug can be used to integrate 'bts show' with the Mutt mail user agent +(unsurprisingly, given the name). To that end, you should define the following +macros in your F<~/.muttrc> (replacing F<~/bin/mutt-fetchbug> for the actual +location of mutt-fetchbug on your system): + + macro index <F7> \ + "<enter-command>unset wait_key<enter><shell-escape>~/bin/mutt-fetchbug --prompt search<enter><change-folder-readonly>~/.cache/mutt_btsresults<enter><enter-command>set wait_key<enter>" \ + "fetch bug(s) (using bts show)" + +The macro (activated by <F7>) will prompt the user for a bug number and then +jump to a temporary mbox showing the fetched bug. + +=head1 SEE ALSO + +mutt(1), bts(1) + +=head1 AUTHOR + +mutt-fetchbug is extensively based off of 'mutt-notmuch', which is +Copyright: (C) 2011 Stefano Zacchiroli <zack@upsilon.cc>. + +All differences between mutt-fetchbug and mutt-notmuch are +Copyright (C) 2012 Ryan Kavanagh <rak@debian.org> + +License: GNU General Public License (GPL), version 3 or higher + +=cut diff --git a/bin/mutt-open b/bin/mutt-open new file mode 100755 index 0000000..0b21261 --- /dev/null +++ b/bin/mutt-open @@ -0,0 +1,76 @@ +#!/bin/bash +# +# Fire up mutt on a given mail, located in some Maildir +# Mail can be specified either by path or by Messsage-ID; in the latter case +# file lookup is performed using some mail indexing tool. +# +# Copyright: © 2009-2014 Stefano Zacchiroli <zack@upsilon.cc> +# License: GNU General Public License (GPL), version 3 or above + +# requires: notmuch | maildir-utils >= 0.7 + +MUTT="neomutt" +MAIL_INDEXER="notmuch" # one of "notmuch", "mu" +export NOTMUCH_CONFIG=$HOME/te/etc/notmuch-config + +MUTT_FLAGS="-R -F ~/te/etc/muttrc" +HIDE_SIDEBAR_CMD="B" # set to empty string if sidebar is not used + +# Sample output of lookup command, which gets passed to mutt-open +# /home/zack/Maildir/INBOX/cur/1256673179_0.8700.usha,U=37420,FMD5=7e33429f656f1e6e9d79b29c3f82c57e:2,S + +die_usage () { + echo "Usage: mutt-open FILE" 1>&2 + echo " mutt-open MESSAGE-ID" 1>&2 + echo 'E.g.: mutt-open `notmuch search --output=files id:MESSAGE-ID`' 1>&2 + echo ' mutt-open `mu find -f l i:MESSAGE-ID`' 1>&2 + echo ' mutt-open 20091030112543.GA4230@usha.takhisis.invalid' 1>&2 + exit 3 +} + +# Lookup: Message-ID -> mail path. Store results in global $fname +lookup_msgid () { + msgid_query="$1" + case "$MAIL_INDEXER" in + notmuch) + fname=$(notmuch search --output=files id:"$msgid_query" | head -n 1) + ;; + mu) + fname=$(mu find -f l i:"$msgid_query" | head -n 1) + ;; + esac +} + +dump_info () { + echo "fname: $fname" + echo "msgid: $msgid" +} + +if [ -z "$1" -o "$1" = "-h" -o "$1" = "-help" -o "$1" = "--help" ] ; then + die_usage +fi +if (echo "$1" | grep -q /) && test -f "$1" ; then # arg is a file + fname="$1" + msgid=$(egrep -i '^message-id:' "$fname" | cut -f 2 -d':' | sed 's/[ <>]//g') +elif ! (echo "$1" | grep -q /) ; then # arg is a Message-ID + msgid="$1" + lookup_msgid "$msgid" # side-effect: set $fname +fi +# dump_info ; exit 3 +if ! dirname "$fname" | egrep -q '/(cur|new|tmp)$' ; then + echo "Path not pointing inside a maildir: $fname" 1>&2 + exit 2 +fi +maildir=$(dirname $(dirname "$fname")) + +if ! [ -d "$maildir" ] ; then + echo "Not a (mail)dir: $maildir" 1>&1 + exit 2 +fi + +# UGLY HACK: without sleep, push keys do not reach mutt, I _guess_ that there +# might be some terminal-related issue here, since also waiting for an input +# with "read" similarly "solves" the problem +sleep 0.5 +mutt_keys="/=i$msgid\n\n$HIDE_SIDEBAR_CMD" +exec $MUTT $MUTT_FLAGS -f "$maildir/" -e "push $mutt_keys" diff --git a/bin/mutt-remember-mail b/bin/mutt-remember-mail new file mode 100755 index 0000000..e9898f5 --- /dev/null +++ b/bin/mutt-remember-mail @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w +# +# Helper for mutt to remember mails in Emacs' Org mode +# +# Copyright: © 2009-2010 Stefano Zacchiroli <zack@upsilon.cc> +# License: GNU General Public License (GPL), version 3 or above +# +# Example of mutt macro to invoke this hitting ESC-R (to be put in ~/.muttrc): +# macro index \eR "|~/bin/remember-mail\n" + +use strict; +use Mail::Internet; +use URI::Escape; + +my $msg = Mail::Internet->new(\*STDIN); +$msg->head->get('message-id') =~ /^<(.*)>$/; +my $mid = $1; +my $subject = $msg->head->get('subject') || ""; +my $from = $msg->head->get('from') || ""; +chomp ($subject, $from); +my $note_body = uri_escape(" Subject: $subject\n From: $from"); + +exec "emacsclient", "-c", "org-protocol:/capture:/m/mutt:$mid/mail/$note_body"; diff --git a/bin/pycombine b/bin/pycombine new file mode 100755 index 0000000..a9d2338 --- /dev/null +++ b/bin/pycombine @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +""" +Copyright 2019 Raúl Benencia + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +""" + + +import argparse +import os +import sys +import re + + +class DirCombiner(): + ignore_files = ['.known', '.gitignore'] + ignore_dirs = ['.git', '.svn', '_darcs'] + + def __init__(self, include, exclude, status_filename, verbose): + self.include = re.compile(include) + self.exclude = re.compile(exclude) + self.status_filename = status_filename + self.verbose = verbose + + def _filter_files(self, filenames): + return [ + fn for fn in filenames + if fn not in DirCombiner.ignore_files + and self.include.match(fn) + and not self.exclude.match(fn) + ] + + def _filter_dirs(self, directories): + return [ + sd for sd in directories + if sd not in DirCombiner.ignore_dirs + and self.include.match(sd) + and not self.exclude.match(sd) + ] + + def _create_symlinks(self, status, dest, dirpath, filenames): + # Create symlinks for each file + for f in filenames: + src = os.path.abspath(os.path.normpath(os.path.join(dirpath, f))) + dst = os.path.normpath(os.path.join(dest, dirpath, f)) + + status.mark_as_seen(dst) + replace_msg = "" + if os.path.lexists(dst): + if not os.path.islink(dst): + sys.stderr.write( + "{} in {} is also in {}\n".format(f, dirpath, dest) + ) + continue + elif os.path.realpath(dst) != src: + replace_msg = "(previously pointing to: {})".format( + os.path.realpath(dst)) + try: + os.remove(dst) + except IOError: + raise DeleteFileError(dst) + else: + # Symlink is already pointing to the current file + continue + + try: + os.symlink(src, dst) + except OSError: + raise CreateSymlinkError(dst) + + if replace_msg == "" or (replace_msg != "" and self.verbose): + sys.stdout.write("{} -> {} {}\n".format(src, dst, replace_msg)) + + def _create_directories(self, dest, dirpath, subdirs): + for sd in subdirs: + dst = os.path.normpath(os.path.join(dest, dirpath, sd)) + if os.path.lexists(dst): + if os.path.isdir(dst): + continue + else: + raise DirIsFileError(dst) + + try: + os.mkdir(dst) + except OSError: + raise CreateDirError(dst) + + sys.stdout.write(f"{dst} created\n") + + def combine(self, dest, directories): + # Loop through all the directories. The later ones can override + # links from the previous ones. + for directory in directories: + try: + os.chdir(directory) + except OSError: + raise ChangeDirError(directory) + + status = DirCombinerStatus(self.status_filename) + for dirpath, subdirs, filenames in os.walk('.'): + # Modify filenames and subdirs in place to reduce the + # scope of the search. + subdirs[:] = self._filter_dirs(subdirs) + filenames[:] = self._filter_files(filenames) + + self._create_symlinks(status, dest, dirpath, filenames) + self._create_directories(dest, dirpath, subdirs) + + status.finish() + + +class DirCombinerStatus(): + def __init__(self, status_filename): + self._seen = [] + self._status_filename = status_filename + + try: + if os.path.exists(self._status_filename): + if not os.path.isfile(self._status_filename): + raise StatusIsNotFileError(self._status_filename) + else: + with open(self._status_filename, 'r') as f: + self._known = f.read().split('\n')[:-1] + else: + self._known = [] + except IOError: + raise StatusFileError(self._status_filename) + + def _clean_old(self): + old = {f for f in self._known if f not in self._seen} + for f in old: + try: + if os.path.lexists(f): + rp = os.path.realpath(f) + if not os.path.islink(f) or os.path.exists(rp): + sys.stderr.write( + f"Not deleting {f} as it's a valid file\n") + else: + os.remove(f) + sys.stdout.write(f"Deleted old file {f}\n") + except IOError: + raise DeleteFileError(f) + + def mark_as_seen(self, filename): + self._seen.append(filename) + + def finish(self): + self._clean_old() + with open(self._status_filename, 'w') as f: + for filename in self._seen: + f.write(filename + '\n') + + +class Error(Exception): + """Base class for exceptions in this program """ + msg = '' + + def __init__(self, param): + self.param = param + + def message(self): + return self.msg.format(self.param) + '\n' + + +class DirIsFileError(Error): + msg = 'Target directory {} already exists as a non-dir file.' + + +class CreateDirError(Error): + msg = 'Failure creating directory {}. Do you have appropriate permissions?' + + +class CreateSymlinkError(Error): + msg = 'Failure creating symlink {}. Do you have appropriate permissions?' + + +class ChangeDirError(Error): + msg = 'Failure changing to dir {}. Do you have appropriate permissions?' + + +class StatusFileError(Error): + msg = 'Error while opening status file {}. ' + \ + 'Do you have appropriate permissions?' + + +class StatusIsNotFile(Error): + msg = 'The status path {} does not have a file. Maybe it holds an old dir?' + + +class DeleteFileError(Error): + msg = 'Unable to delete file {}. Do you have appropriate permissions?' + + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument('-i', '--include', default='.*', + help='regex for filenames to include') + + parser.add_argument('-e', '--exclude', default='^$', + help='regex for filenames to exclude') + + parser.add_argument('-s', '--status-filename', default='.known', + help='regex for filenames to exclude') + + parser.add_argument('dest', metavar='dest', type=str, + help='directory where to install links and filenames') + + parser.add_argument('dirs', metavar='dir', type=str, nargs='+', + help='directories to retrieve filenames from') + + parser.add_argument('-v', '--verbose', default=False, + help='Verbose output') + + + args = parser.parse_args() + dc = DirCombiner(args.include, args.exclude, args.status_filename, args.verbose) + + try: + dc.combine(args.dest, args.dirs) + + except Error as e: + sys.stderr.write(e.message()) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/bin/touchpad-enable-tap b/bin/touchpad-enable-tap new file mode 100755 index 0000000..dcd530f --- /dev/null +++ b/bin/touchpad-enable-tap @@ -0,0 +1,7 @@ +#!/bin/sh + +if xinput list | grep -iq "touchpad"; then + device_id=$(xinput list | grep -i touchpad | awk '{print $6}' | sed 's/id=//') + option_id=$(xinput list-props $device_id | grep -i 'Tapping Enabled' | head -n1 | awk '{print $4}' | tr -d '():') + xinput set-prop $device_id $option_id 1 +fi diff --git a/bin/xrandr-rul b/bin/xrandr-rul new file mode 100755 index 0000000..0b76a98 --- /dev/null +++ b/bin/xrandr-rul @@ -0,0 +1,21 @@ +#!/bin/sh + +INT="eDP-1" + +export DISPLAY="${DISPLAY:-:0}" + +xrandr --output ${INT} --mode 1920x1080 # 1920x1440 #2560x1440 + +for EXT in DP-1 DP-2 HDMI-2 +do + if xrandr -q | grep -qs "^${EXT} connected" + then + xrandr --output ${EXT} --mode 3440x1440 + xrandr --output ${EXT} --left-of ${INT} + else + xrandr --output ${EXT} --off + fi +done + +# If we have a script for sorting out the workspaces, use it. +eval "$(command -v i3-fix-workspaces)" |