-
-
Save alexklapheke/390ca44559715f93cdf0598531b724fc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Copyright (C) 2016 Alexander Klapheke | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining | |
# a copy of this software and associated documentation files (the "Software"), | |
# to deal in the Software without restriction, including without limitation | |
# the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
# and/or sell copies of the Software, and to permit persons to whom the | |
# Software is furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included | |
# in all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
# Simple academic paper queue, inspired by paperq <paperq.invergo.net> | |
# but with tab completion, and pandoc-citeproc as a more robust bibtex | |
# parser. Expects papers with the same filenames as their bibtex keys. | |
# Requires pandoc v1.16. Released under the MIT License. | |
# | |
# TODO: would be much faster if --list could parse all bibtex keys at once | |
# TODO: if a key is a prefix of an earlier key, you can't access it---try to match exact first? | |
# TODO: nicer errors if citation not found (esp. in bibshort() ) | |
# TODO: replace biblong()/bibshort() voodoo with actual parser | |
# Options: | |
bibfile=$HOME/texmf/bibtex/bib/references.bib | |
paperdir=$HOME/Documents/Papers # no trailing slash | |
queue=$HOME/.config/qlist | |
default_action=list # remove, view, bib, list, zip, count, edit | |
view_program=xdg-open # program used to view PDFs | |
[[ -t 1 ]] && colors=1 # color output if running interactively | |
[[ -f "$queue" ]] || touch "$queue" | |
# color messages | |
message () { if [[ $colors ]]; then echo -e "\e[1;30m${*}\e[0m" >&2; else echo "$*" >&2; fi } | |
error () { if [[ $colors ]]; then echo -e "\e[1;31m${*}\e[0m" >&2; else echo "$*" >&2; fi } | |
# color output | |
gray () { if [[ $colors ]]; then sed $'s/.\+/\e[1;30m\\0\e[0m/' ; else sed -e ''; fi } | |
white () { if [[ $colors ]]; then sed $'s/.\+/\e[1;37m\\0\e[0m/' ; else sed -e ''; fi } | |
plain () { if [[ $colors ]]; then sed $'s/.\+/\e[0;37m\\0\e[0m/' ; else sed -e ''; fi } | |
# check to make sure all external commands can be called | |
executables=("pandoc" "pandoc-citeproc" "zip" "$view_program") | |
for ((i=0; i < ${#executables[@]}; i++)); do | |
if [[ ! $(command -v ${executables[$i]}) ]]; then | |
error "Executable not found: ${executables[$i]}"; exit | |
fi | |
done | |
# format citations | |
biblong () { | |
echo "@$1" | pandoc --bibliography="$bibfile" -t plain --columns="$(tput cols)" | tail -n +3 | head -n -2; | |
} | |
bibshort () { | |
echo "@$1" | pandoc --bibliography="$bibfile" -t plain --wrap=none | # format citation | |
white | # color author+date | |
sed '2,$s/.*\(“[^”]\+”\).*/\1/' | # extract title | |
tr '\n' ' ' | # join lines | |
sed "s/\(.\{$(($(tput cols)-(number_width-number_space)-6))\}\).*/\1.../"; # truncate | |
} | |
get_paper_id () { | |
local id=${1:-1} | |
if [[ $id =~ ^[[:digit:]]+$ ]]; then | |
if [[ $id -le 0 || $id -gt $(queuelength) ]]; then | |
error "Invalid index." | |
exit 1 | |
fi | |
id=$(sed -n "${id}p" "$queue") | |
else | |
if ! grep -q "$id" "$queue"; then | |
error "Invalid index." | |
exit 1 | |
fi | |
id=$(sed -n "/${id}/{p;q}" "$queue") | |
fi | |
echo "$id" | |
} | |
queuelength () { | |
wc -l "$queue" | cut -f 1 -d ' ' | |
} | |
iterate () { | |
if [[ $(queuelength) -eq 0 ]]; then | |
message "Queue is empty! For help, run: $(basename "$0") --help" | |
return 1 | |
fi | |
local num=$(echo "$1" | bc) | |
while read -r line; do | |
if [[ "$line" = "" ]]; then continue; fi | |
if [[ $num -ne 0 && $i -gt $num ]]; then break; fi | |
echo "$line" | |
i=$((i+1)) | |
done < "$queue" | |
} | |
addpaper () { | |
local id=$1 | |
filename="$paperdir/$id.pdf" | |
if [[ ! -r "$filename" ]]; then | |
error "Could not read file $filename" | |
return 1 | |
fi | |
echo "$id" >> "$queue" | |
message "Added paper:" | |
biblong "$id" | |
} | |
removepaper () { | |
local id=$(get_paper_id "$1") | |
if [[ $id ]]; then | |
sed -i "/${id}/{d;q}" "$queue" | |
message "Removed paper:" | |
biblong "$id" | |
fi | |
} | |
viewpaper () { | |
local id=$(get_paper_id "$1") | |
if [[ $id ]]; then | |
"$view_program" "$paperdir/$id.pdf" &> /dev/null | |
fi | |
} | |
listpapers () { | |
local i=1 | |
local num=$1 | |
# Width of list numbers (= log_10 of number of list items to show) | |
number_width=$(echo "$(echo "l(${num:-$(queuelength)})/l(10)" | bc -l)/1" | bc) | |
# Width of space after numbers (bash counts the color codes as taking up width) | |
number_space=$(if [[ $colors ]]; then echo 12; else echo 1; fi) | |
for id in $(iterate "${num:=0}"); do | |
echo -e "$(printf "%-$((number_width+number_space))b" "$(echo -n $i | gray)") $(bibshort "$id")" | |
i=$((i+1)) | |
done | |
} | |
zippapers () { | |
local i=1 | |
local num=$1 | |
temp=$(mktemp -d) | |
for id in $(iterate "${num:=0}"); do | |
ln -T "${paperdir}/${id}.pdf" "${temp}/$(printf '%02d\n' "$i")-${id}.pdf" | |
i=$((i+1)) | |
done | |
zip -j ./q.zip "$temp"/* | |
[[ -d "$temp" ]] && rm -rf "$temp" | |
} | |
if [[ $# -eq 0 ]]; then set "$1" "--$default_action"; fi | |
while [[ $# -gt 0 ]]; do | |
opt="$1"; shift; if [[ ! "$1" =~ ^- ]]; then arg="$1"; shift; fi | |
case "$opt" in | |
'-a'|'--add' ) addpaper "${arg:?Please specify a filename}" ;; | |
'-r'|'--remove') removepaper "$arg" ;; | |
'-v'|'--view' ) viewpaper "$arg" ;; | |
'-b'|'--bib' ) biblong "$(get_paper_id "$arg")" ;; | |
'-l'|'--list' ) listpapers "$arg" ;; | |
'-z'|'--zip' ) zippapers "$arg" ;; | |
'-c'|'--count' ) queuelength | gray ;; | |
'-e'|'--edit' ) "$EDITOR" "$queue" ;; | |
'-h'|'--help' ) | |
echo "Reading queue" | |
echo "Usage: $(basename "$0") [OPTIONS]" | |
echo | |
echo " -h, --help Print this message" | |
echo | |
echo " -a, --add ID Add (enqueue) file with BibTeX key ID to end of queue" | |
echo " -r, --remove [NUM] Remove (dequeue) file from queue (default: first)" | |
echo " -v, --view [NUM] View file from queue (default: first)" | |
echo " -b, --bib [NUM] View bibliography entry for file (default: first)" | |
echo " -l, --list [NUM] List first NUM files (default: all)" | |
echo " -z, --zip [NUM] Create archive of first NUM files (default: all)" | |
echo " -c, --count Count files in queue" | |
echo " -e, --edit Manually edit queue" | |
echo | |
echo "Options can be used in sequence. With no options, default action is ${default_action}." | |
echo "A bare argument is interpreted as a file to be viewed." | |
echo "-a completes a file base name with the extension .pdf." | |
echo "-v, -b, -r also accept parts of filenames and select the first matching file." | |
echo | |
exit 0 ;; | |
-*) error "Inalid option: $opt" ;; | |
[^-]*) viewpaper "$opt" ;; | |
esac | |
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Completion: Place the following completion file in /etc/bash_completion.d/: | |
_papers () { | |
COMPREPLY=() | |
local cur=${COMP_WORDS[COMP_CWORD]} | |
local prev=${COMP_WORDS[COMP_CWORD-1]} | |
local paperdir=$HOME/Documents/Papers # no trailing slash | |
local queue=$HOME/.config/qlist | |
if [[ "$cur" = -* ]]; then | |
COMPREPLY=( $( compgen -W "-h --help -a --add -r --remove -v --view -b --bib -l --list -z --zip -c --count -e --edit" -- $cur ) ) | |
else | |
case "$prev" in | |
-a|--add) COMPREPLY=( $( compgen -W "$(ls "$paperdir" | xargs basename -s '.pdf')" -- $cur ) ) ;; | |
-r|--remove|-v|--view|-b|--bib|q) COMPREPLY=( $( compgen -W "$(cat "$queue")" -- $cur ) ) ;; | |
esac | |
fi | |
} | |
complete -F _papers q |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment