aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rwxr-xr-xbin/adjust_dpms55
-rwxr-xr-xbin/androidbackup.sh143
-rwxr-xr-xbin/atmux122
-rwxr-xr-xbin/autostart23
-rwxr-xr-xbin/backup-settings19
-rwxr-xr-xbin/check-rt11
-rwxr-xr-xbin/check-vanilla11
-rwxr-xr-xbin/checkip4
-rw-r--r--bin/connect_to_jack12
-rwxr-xr-xbin/flac2mp319
-rwxr-xr-xbin/functions.sh176
-rwxr-xr-xbin/gpg2mutt69
-rw-r--r--bin/letter71
-rwxr-xr-xbin/lid-switch-action198
-rwxr-xr-xbin/lightdm-display-setup4
-rwxr-xr-xbin/lowercase23
-rwxr-xr-xbin/mirssi12
-rwxr-xr-xbin/monitor-hotplug7
-rwxr-xr-xbin/notify_irssi_server.pl62
-rwxr-xr-xbin/notify_mpd42
-rwxr-xr-xbin/nouveau2nvidia57
-rwxr-xr-xbin/nvidia2nouveau58
-rwxr-xr-xbin/nvsetup10
-rwxr-xr-xbin/pacman-disowned16
-rwxr-xr-xbin/pacoptpd14
-rwxr-xr-xbin/pass2msmtp3
-rw-r--r--bin/pass2offlineimap5
-rwxr-xr-xbin/pass2vdirsyncer4
-rwxr-xr-xbin/rmcache32
-rwxr-xr-xbin/rollback-profile69
-rwxr-xr-xbin/run_once4
-rwxr-xr-xbin/sclang10
-rwxr-xr-xbin/screensetup17
-rwxr-xr-xbin/secret41
-rwxr-xr-xbin/set_backlight56
-rwxr-xr-xbin/set_volume142
-rw-r--r--bin/stikked115
-rwxr-xr-xbin/stop_jack16
-rwxr-xr-xbin/switch_wfs_os91
-rw-r--r--bin/syncmpdpl209
-rwxr-xr-xbin/tex2pdf33
-rwxr-xr-xbin/texcount.pl1391
-rw-r--r--bin/torrentSort.py220
43 files changed, 3696 insertions, 0 deletions
diff --git a/bin/adjust_dpms b/bin/adjust_dpms
new file mode 100755
index 0000000..c8e8a0f
--- /dev/null
+++ b/bin/adjust_dpms
@@ -0,0 +1,55 @@
+#!/bin/bash
+# Script to adjust dpms settings when running certain programs,
+# that don't use fullscreen, but should not be "blanked" in between.
+# Programs are defined in separate config file ~/.config/dpms
+# Run as timed user script (i.e. adjust_dpms@.service) with systemd
+# from /etc/systemd/system/timer-minutely.target.wants
+
+export DISPLAY=:0.0
+
+config="/home/$(whoami)/.config/dpms"
+blankoff=0
+
+# check for config file existence
+if [[ -f $config ]];then
+ . /home/dave/.config/dpms
+else
+ echo "Config file not readable $config"
+ exit 1
+fi
+
+# loop all programs and check for running instances
+for i in "${programs[@]}"
+do
+ pid=$(pidof -x $i)
+ re='^[0-9]+$'
+ if [[ $pid =~ $re ]]; then
+ echo "$i running ($pid)"
+ blankoff=1
+ fi
+done
+
+# if valuable programs are running, don't use screen blanking
+if [ $blankoff -gt 0 ]; then
+ echo "Some program requires screensaver to be off."
+ # print currently set values for screensaver timeout to tmp file
+ echo "s $(xset q|grep timeout|awk '{print $2}') $(xset q|grep timeout|awk '{print $4}')" > $adjust_dpms
+ echo "dpms $(xset q|grep Standby|awk '{print $2}') $(xset q|grep Standby|awk '{print $4}') $(xset q|grep Standby|awk '{print $6}')" >> $adjust_dpms
+ # disable screensaver and dpms
+ xset s off
+ xset -dpms
+else
+ xsetq=$(xset q|grep timeout|awk '{print $2}')
+ # if the timeout is still 0, set it to its former value, or just switch on screensaver/dpms again
+ if [[ $xsetq -eq 0 ]];then
+ if [[ -f "$adjust_dpms" ]];then
+ xset $(head -n1 $adjust_dpms)
+ xset $(tail -n1 $adjust_dpms)
+ rm -rf $adjust_dpms
+ else
+ xset +dpms
+ xset s on
+ fi
+ fi
+fi
+
diff --git a/bin/androidbackup.sh b/bin/androidbackup.sh
new file mode 100755
index 0000000..ee95f01
--- /dev/null
+++ b/bin/androidbackup.sh
@@ -0,0 +1,143 @@
+#!/bin/bash
+
+if [ -e $HOME/bin/functions.sh ]
+then
+ . $HOME/bin/functions.sh
+else
+ exit 0
+fi
+
+## CONFIGURATION
+ADB=adb
+
+function help()
+{
+ echo "Android data backup/restore"
+ echo "Usage:"
+ echo -e "\t [backup|restoredata|restoreapk] [device]"
+ exit 0
+}
+
+function _backup()
+{
+ adb -s $DEVICE pull /data/system/packages.list .
+ cat packages.list | while read line
+ do
+ read name dir <<< $(echo $line | awk '{ print $1 " " $4 }')
+ #echo $dir
+ apk=$($ADB shell bash -c "ls /data/app/${name}* 2> /dev/null")
+ if [ -n $exists ]
+ then
+ echo $name $apk
+ else
+ echo LALALALALAAA $name
+ fi
+ echo ""
+ done
+}
+
+function _restoreapk() {
+ mkdir -p done skipped
+ apps=$(ls -1 | grep -c apk)
+ num=1
+ for i in *.apk
+ do
+ if [ $i = "done" -o $i = "skipped" ]
+ then
+ continue
+ fi
+ echo_green "Application $i ($num/$apps)"
+
+ name=$(echo $i | sed 's/-[0-9].apk//')
+ existing=$($ADB shell sh -c "ls /data/app*/${name}* 2> /dev/null")
+ echo $existing
+ if [ -n "$existing" ]
+ then
+ echo $i already installed
+ mv $i skipped
+ continue
+ fi
+ echo " Restore apk? [1|0] (default 1)"
+ echo -en " "
+ read inst
+ inst=${inst:=1}
+
+ if [ $inst -eq 1 ]
+ then
+ $ADB install $i
+ mv $i done
+ else
+ echo -e " Skipping...\n\n"
+ mv $i skipped
+ fi
+ let num=num+1
+ done
+}
+
+function _restoredata()
+{
+ mkdir -p done skipped
+ apps=$(ls -l | grep -v skipped | grep -v done | grep -c '^d')
+ num=1
+ for i in *
+ do
+ if [ $i = "done" -o $i = "skipped" ]
+ then
+ continue
+ fi
+
+ echo_green "Application $i ($num/$apps)"
+ if [ $(adb shell ls -l /data/data | grep -c $i) -lt 1 ]
+ then
+ echo_red " not installed, skipping.\n\n"
+ mv $i skipped
+ continue
+ fi
+
+ echo " Restore data? [1|0] (default 0)"
+ echo -en " "
+ read inst
+ inst=${inst:=0}
+
+ if [ $inst -eq 1 ]
+ then
+ if [ $i = "com.android.providers.telephony" ]
+ then
+ echo -e "SMS/CALLS"
+ $ADB push $i/databases/mmssms.db /data/data/$i/databases
+ $ADB shell chmod 660 /data/data/$i/databases/mmssms.db
+ $ADB shell toolbox chown 1001:1001 /data/data/$i/databases/mmssms.db
+ $ADB shell killall system_server
+ mv $i done
+ else
+ own=$($ADB shell ls -l /data/data/ | grep $i | head -n 1 | awk '{ print $3}')
+ echo -e " User $own\n\n"
+ mute $ADB push $i /data/data/$i
+ mute $ADB shell chown $own:$own /data/data/$i/*
+ mute $ADB shell chown $own:$own /data/data/$i/*/*
+ mute $ADB shell chown $own:$own /data/data/$i/*/*/*
+ mv $i done
+ fi
+ else
+ echo -e " Skipping...\n\n"
+ mv $i skipped
+ fi
+ let num=num+1
+ done
+}
+
+case $1 in
+ backup)
+ _backup
+ ;;
+ restoredata)
+ _restoredata
+ ;;
+ restoreapk)
+ _restoreapk
+ ;;
+ *)
+ help
+ exit 0
+ ;;
+esac
diff --git a/bin/atmux b/bin/atmux
new file mode 100755
index 0000000..7601693
--- /dev/null
+++ b/bin/atmux
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+if [ -e $HOME/bin/functions.sh ]
+then
+ . $HOME/bin/functions.sh
+else
+ echo "functions.sh not found"
+ exit 0
+fi
+
+# override tmux
+tmux='tmux'
+
+## CONFIGURATION
+# session name
+sn=${1:-atmux}
+
+# try to attach earlier session
+tmux attach-session -t $sn && exit 0
+#or continue
+
+# default path
+case $sn in
+ # window definitions
+ # path:command:custom-name
+ "thesis")
+ dpath=/home/konni/dropbox/documents/uni/12ss/BA/breite-quellen/thesis
+ windows="-:-"
+ windows+="matlab:echo bla"
+ ;;
+ "mako")
+ dpath=/extra/src/cyanogenmod-10.1
+ dcmd=". environment_mako"
+ windows="-:-"
+ windows+="-:-"
+ windows+=" device/lge/mako:-"
+ windows+=" out/target/product/mako:-"
+ ;;
+ "ville_dev")
+ dpath=/extra/src/cyanogenmod-10.1
+ dcmd=". environment_ville"
+ windows="-:-"
+ windows+=" -:-"
+ windows+=" device/htc/ville:-"
+ windows+=" device/htc/msm8960-common:-"
+ windows+=" kernel/htc/msm8960:-"
+ windows+=" vendor/htc:-"
+ windows+=" out/target/product/ville:-"
+ ;;
+ "ville")
+ dpath=/extra/src/cyanogenmod-jellybean
+ dcmd=". environment_ville"
+ windows="-:-"
+ windows+=" -:-"
+ windows+=" device/htc/ville:-"
+ windows+=" device/htc/msm8960-common:-"
+ windows+=" kernel/htc/msm8960:-"
+ windows+=" vendor/htc:-"
+ windows+=" out/target/product/ville:-"
+ ;;
+ *)
+ dpath=$HOME
+ windows="-:-"
+esac
+
+
+# enter default path
+mute pushd $dpath
+
+# prepare windows
+num=1
+for window in $windows
+do
+ # parse configuration
+ read wpath wcmd <<< $(echo $window | sed 's/:/\ /g')
+
+ # set path
+ unset tpath
+ if [ $wpath != "-" ]
+ then
+ tpath=$wpath
+ fi
+
+
+ if [ $num -eq 1 ]
+ then
+ # start new session
+ $tmux new-session -d -s "$sn" $tname
+ # set default path for new windows
+ $tmux set-option -t "$sn" default-path $dpath
+ else
+ # create new window in session
+ $tmux new-window -t "$sn:$num" $tname
+ fi
+
+ # execute default command
+ if [ -n "$dcmd" ]
+ then
+ $tmux send-keys -t "$n:$num" "$dcmd" C-m
+ fi
+
+ # change path
+ if [ -n "$tpath" ]
+ then
+ $tmux send-keys -t "$n:$num" "cd $tpath" C-m
+ fi
+
+ # execute custom command
+ unset tcmd
+ if [ "$wcmd" != "-" ]
+ then
+ tcmd=$wcmd
+ $tmux send-keys -t "$n:$num" "$tcmd" C-m
+ fi
+
+ let num=num+1
+done
+
+# select window #1 and attach to session
+$tmux select-window -t "$sn:1"
+$tmux attach-session -t "$sn"
+
diff --git a/bin/autostart b/bin/autostart
new file mode 100755
index 0000000..9d70ad0
--- /dev/null
+++ b/bin/autostart
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Auto starting various programs post lightdm
+
+export DISPLAY=:0
+export XAUTHORITY=/home/dave/.Xauthority
+
+KERNEL_NAME=$(uname -r)
+if [[ "$KERNEL_NAME" != *rt* ]]; then
+ pid=$(pidof dropbox)
+ re='^[0-9]+$'
+ if [[ $pid =~ $re ]];then
+ echo "Dropbox alread running. Restarting..."
+ dropbox stop && dropbox start &
+ else
+ dropbox start &
+ fi
+# pulseaudio --start &
+# nm-applet &
+# firewall-applet &
+else
+ pulseaudio --kill &
+ qjackctl &
+fi
diff --git a/bin/backup-settings b/bin/backup-settings
new file mode 100755
index 0000000..1a9cc12
--- /dev/null
+++ b/bin/backup-settings
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+etcTMP=/tmp/etc.tgz
+etcIF=/etc
+OF=/home/dave/ownCloud/backup/settings
+gpgEND=.gpg
+
+echo "Backing up /etc directory."
+tar cfPz $etcTMP $etcIF
+echo "Encrypting compressed directory."
+su - dave -c "gpg -e -r 'David Runge <david.runge@frqrec.com>' $etcTMP"
+echo "Changing permissions on file."
+chown dave:dave $etcTMP$gpgEND
+echo "Moving backup to ownCloud."
+mv -f $etcTMP$gpgEND $OF
+echo "Cleaning up."
+rm $etcTMP
+echo "Done."
+
diff --git a/bin/check-rt b/bin/check-rt
new file mode 100755
index 0000000..5dc04cd
--- /dev/null
+++ b/bin/check-rt
@@ -0,0 +1,11 @@
+#!/bin/env bash
+#Check if the kernel name has -rt in it (we're running Realtime Kernel)
+
+if [[ $(uname -r) == *-rt* ]];then
+ echo "Success: Running $(uname -r)."
+ exit 0
+else
+ echo "Failure: Running $(uname -r)."
+ exit 1
+fi
+
diff --git a/bin/check-vanilla b/bin/check-vanilla
new file mode 100755
index 0000000..6b4bdd4
--- /dev/null
+++ b/bin/check-vanilla
@@ -0,0 +1,11 @@
+#!/bin/env bash
+#Check if the kernel name has -rt in it (we're running Realtime Kernel)
+
+if [[ $(uname -r) == *-rt* ]];then
+ echo "Failure: Running $(uname -r)."
+ exit 1
+else
+ echo "Success: Running $(uname -r)."
+ exit 0
+fi
+
diff --git a/bin/checkip b/bin/checkip
new file mode 100755
index 0000000..0e9d345
--- /dev/null
+++ b/bin/checkip
@@ -0,0 +1,4 @@
+#!/bin/bash
+#echo "$(curl -s http://checkip.dyndns.org | sed 's/[a-zA-Z/<> :]//g')
+#wget -qO- http://ipecho.net/plain ; echo
+dig +short myip.opendns.com @resolver1.opendns.com
diff --git a/bin/connect_to_jack b/bin/connect_to_jack
new file mode 100644
index 0000000..e17ccc2
--- /dev/null
+++ b/bin/connect_to_jack
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+case "$HOSTNAME" in
+ "pitheunlord")
+ jack_connect SuperCollider:out_1 alsa_out:playback_1 &
+ jack_connect SuperCollider:out_2 alsa_out:playback_2 &
+ ;;
+ "beagleclone")
+ ;;
+ "dvzrv")
+ ;;
+esac
diff --git a/bin/flac2mp3 b/bin/flac2mp3
new file mode 100755
index 0000000..9dd970b
--- /dev/null
+++ b/bin/flac2mp3
@@ -0,0 +1,19 @@
+find -name *.flac -print0 | while read -d $'\0' a
+
+
+do
+OUTF=`echo "$a" | sed s/\.flac$/.mp3/g`
+
+ARTIST=`metaflac "$a" --show-tag=ARTIST | sed s/.*=//g`
+TITLE=`metaflac "$a" --show-tag=TITLE | sed s/.*=//g`
+ALBUM=`metaflac "$a" --show-tag=ALBUM | sed s/.*=//g`
+GENRE=`metaflac "$a" --show-tag=GENRE | sed s/.*=//g`
+TRACKNUMBER=`metaflac "$a" --show-tag=TRACKNUMBER | sed s/.*=//g`
+DATE=`metaflac "$a" --show-tag=DATE | sed s/.*=//g`
+
+flac -c -d "$a" | lame -m j -q 0 --vbr-new -V 0 -s 44.1 - "$OUTF"
+id3 -t "$TITLE" -T "${TRACKNUMBER:-0}" -a "$ARTIST" -A "$ALBUM" -y "$DATE" -g "${GENRE:-12}" "$OUTF"
+
+done
+
+
diff --git a/bin/functions.sh b/bin/functions.sh
new file mode 100755
index 0000000..951def5
--- /dev/null
+++ b/bin/functions.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+# some functions
+
+# zsh color output
+# echo "$FG[46]$FX[blink]Hello, World"
+
+function echo_green() {
+ echo -e '\E[1;32m'$*'\E[0m'
+}
+
+function echo_red() {
+ echo -e '\E[1;31m'$*'\E[0m'
+}
+
+function echo_magenta() {
+ echo -e '\E[1;35m'$*'\E[0m'
+}
+
+function echo_cyan() {
+ echo -e '\E[1;36m'$*'\E[0m'
+}
+
+# log text ...
+function log() {
+ date=`date "+%Y-%m-%d %H:%M:%S"`
+ if [ -n $VERBOSE ]
+ then
+ echo $date $@ | tee -a $LOGFILE
+ fi
+ [ -n $LOGFILE ] && echo $date $@ >> $LOGFILE
+}
+
+function log_green() {
+ date=`date "+%Y-%m-%d %H:%M:%S"`
+ if [ -n $VERBOSE ]
+ then
+ echo_green $date $@
+ fi
+ [ -n $LOGFILE ] && echo $date $@ >> $LOGFILE
+}
+
+function log_red() {
+ date=`date "+%Y-%m-%d %H:%M:%S"`
+ if [ -n $VERBOSE ]
+ then
+ echo_red $date $@
+ fi
+ [ -n $LOGFILE ] && echo $date $@ >> $LOGFILE
+}
+
+function log_magenta() {
+ date=`date "+%Y-%m-%d %H:%M:%S"`
+ if [ -n $VERBOSE ]
+ then
+ echo_magenta $date $@
+ fi
+ [ -n $LOGFILE ] && echo $date $@ >> $LOGFILE
+}
+
+function log_cyan() {
+ date=`date "+%Y-%m-%d %H:%M:%S"`
+ if [ -n $VERBOSE ]
+ then
+ echo_cyan $date $@
+ fi
+ [ -n $LOGFILE ] && echo $date $@ >> $LOGFILE
+}
+
+function notify() {
+ title="$1"
+ text="$2"
+ echo 'naughty.notify({title = "'$title'", text = "'$text'"})' | awesome-client
+}
+
+# mute command
+function mute() {
+ $@ > /dev/null 2> /dev/null
+}
+
+# run pidfile command
+function run() {
+ pidfile=$1
+ shift
+ command=$@
+
+ exec $command > /dev/null 2> /dev/null &
+ pid=$!
+ sleep 0.25
+ npid=$(pidof -s $1)
+ if [ -n "$npid" ] && [ $npid -ne $pid ]
+ then
+ pid=$npid
+ fi
+ echo $pid > $pidfile
+ log_green executed $command"\n"
+}
+
+# run_once $pidfile $command[]
+function run_once() {
+ pidfile=/tmp/run/lock/$1
+ shift
+ command=$@
+
+ mkdir -p $(dirname $pidfile)
+
+ if [ -f $pidfile ];
+ then
+ pid=`head -n 1 $pidfile`
+ running=$(ps -u $(whoami) -p $pid | grep '^[ ]*'$pid)
+ if [ -z "$running" ]
+ then
+ log_cyan stale pidfile found
+ rm -f $pidfile
+
+ npid=$(pidof -s $1)
+ if [ -n "$npid" ]
+ then
+ log_green "pidfile updated\n"
+ echo $npid > $pidfile
+ else
+ run $pidfile $command
+ fi
+
+ else
+ log_cyan "active pidfile found\n"
+ fi
+ else
+ pid=`ps | grep "$command" | grep -v grep | head -n 1 | awk '{ print $1 }'`
+ if [ -z "$pid" ]
+ then
+ log_green not running
+ run $pidfile $command
+ else
+ log_cyan "pidfile updated\n"
+ echo $pid > $pidfile
+ fi
+ fi
+}
+
+# run_auto $file
+function run_auto() {
+ file=$1
+ . $file
+
+ cur_host=`hostname`
+ case $cur_host in
+ annoyance|silence|remembrance)
+ cur_area=private
+ ;;
+ c*|s*)
+ cur_area=unixpool
+ ;;
+ esac
+
+ if [ $area != $cur_area -a $area != "any" ]
+ then
+ log_magenta invalid_area $(basename $file)"\n"
+ return
+ fi
+
+ if [ $host != $cur_host -a $host != "any" ]
+ then
+ log_magenta invalid_host $(basename $file)"\n"
+ return
+ fi
+
+ if [ $allow_multiple -eq 1 ]
+ then
+ log_green run $(basename $file)
+ run /dev/null $command
+ else
+ log_green run_once $(basename $file)
+ run_once `basename $file` $command
+ fi
+}
+
diff --git a/bin/gpg2mutt b/bin/gpg2mutt
new file mode 100755
index 0000000..d9db27b
--- /dev/null
+++ b/bin/gpg2mutt
@@ -0,0 +1,69 @@
+#!/bin/bash
+# ~/.mutt/generate_pgp_auto
+# Generate mutt pgp_auto* send-hooks from gpg pubring.
+# Redirect output to file and source that in muttrc.
+# Add the global hook _before_ sourcing the list:
+# send-hook . 'reset pgp_autoencrypt'
+# -=*# created by erAck #*=- CopyLeft Eike Rathke 2008-01-08T01:36+0100
+
+# At least in an UTF-8 environment sed gets confused by 8-bit characters in
+# real names and doesn't match the address anymore, an empty LANG variable
+# works around.
+LANG=
+
+# Output file
+output="$HOME/.mutt/gpg-auto.rc"
+
+# if the file exists, delete it
+if [ -f "${output}" ]; then
+ rm "${output}"
+fi
+
+# 2nd gpg colon field:
+# d := disabled (deprecated - use the 'D' in field 12 instead)
+# e := expired
+# r := revoked
+
+# Note that the following lines are part of the sed script passed by the shell
+# and may not contain the ' character! Hence the double backslash in mail
+# addresses to escape the regex . dot meta character for Mutt.
+#gpg --list-keys --with-colons --fixed-list-mode --no-secmem-warning
+gpg --list-keys --with-colons --fixed-list-mode --no-secmem-warning | sed -ne '
+
+:START
+
+# ignore d|e|r keys
+/^pub:[der]:/ b IGNORE
+
+# ignore disabled keys, D in last field (12)
+/^pub:.*D[^:]*:$/ b IGNORE
+
+# take keys with encryption capability (E in last field), ignore without and
+# other records like ^tru:
+#/^pub:.*E[^:]*:$/ ! b IGNORE
+
+# extract uids and convert address to mutt hook and print
+:EXTRACT
+# ignore non-uid or no address
+/^uid:[^der]:[^<]*<\([^:>]\+@[^:>]\+\)>/ ! b NUSKIP
+# extract address
+# somehow the colon part after \)> is needed to not produce a trailing : in output
+# sed buffer problem?
+s/^uid:[^der]:[^<]*<\([^:>]\+@[^:>]\+\)>[^:]*:/\1/
+# escape dot meta characters, with escaped backslash for mutt
+s/\./\\\\./g
+# print hook
+s/\(.*\)/send-hook "!~l ~t \1" "set crypt_autoencrypt"/p
+:NUSKIP
+n
+/^pub:/ b START
+b EXTRACT
+
+# ignore entire key with uid/sub/... until next pub is encountered
+:IGNORE
+n
+/^pub:/ b START
+b IGNORE
+
+' | egrep -v 'WhatYouDontWantInThisList@example\\\\\.org' | sort -u > ${output}
+# Note the triple escaped backslash!
diff --git a/bin/letter b/bin/letter
new file mode 100644
index 0000000..87e2c34
--- /dev/null
+++ b/bin/letter
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Create a letter
+
+# Configuration
+template=~/documents/letter_template.tex
+dir=~/documents/`date +%Y`
+texdir=$dir/tex
+editor=/usr/bin/vim
+reader=/usr/bin/mupdf
+
+function mkpdf () {
+ pdflatex -shell-escape $1.tex
+ EXT=(aux log)
+ for i in ${EXT[*]}
+ do
+rm -v $1.$i
+ done
+}
+
+# Preparation
+mkdir -p $dir
+mkdir -p $texdir
+
+named=0
+while [ $named -eq 0 ]
+do
+read -p "Enter document name: " REPLY
+ docname=`echo $REPLY | sed 's/\ /_/g'`
+ docdate="`date +%Y-%m-%d`"
+ doc=$dir/${docdate}_${docname}
+ tex=$dir/${docdate}_${docname}.tex
+ pdf=$dir/${docdate}_${docname}.pdf
+ if [ -e $pdf ]
+ then
+read -p "Document already exists. Overwrite? (y/N) " REPLY
+ [ "$REPLY" == "y" ] || continue
+fi
+
+read -p "Create document \"$pdf\"? (Y/n) " REPLY
+ [ "$REPLY" == "y" -o x"$REPLY" == x ] && named=1
+done
+
+cd $dir
+cp $template $tex
+
+$editor $tex
+mkpdf $doc
+$reader $pdf
+
+finished=0
+while [ $finished -eq 0 ]
+do
+read -p "Finished editing? (Y/n) " REPLY
+ if [ "$REPLY" == "y" -o x"$REPLY" == x ]
+ then
+finished=1
+ else
+ $editor $tex
+ mkpdf $doc
+ $reader $pdf
+ fi
+done
+
+read -p "Keep a copy of the .tex file? (Y/n) " REPLY
+if [ "$REPLY" == "y" -o x"$REPLY" == x ]
+then
+mv $tex $texdir
+else
+rm $tex
+fi
+
diff --git a/bin/lid-switch-action b/bin/lid-switch-action
new file mode 100755
index 0000000..a2779b7
--- /dev/null
+++ b/bin/lid-switch-action
@@ -0,0 +1,198 @@
+#!/bin/bash
+# What action to take on closing the laptop lid,
+# depending on whether a screen is attached or not.
+# This implies setting HandleLidSwitch=ignore in /etc/systemd/logind.conf
+# Call this script from /etc/acpi/handler.sh (or other properly set up script
+# for handling button/lid acpi event) with parameter 0 and 1 for close and open respectively
+
+set -e -u
+
+# Find current Xsession user through loginctl list-sessions (excluding lightdm)
+# TODO: make available for desktop manager, too!
+declare x_user="$(loginctl list-sessions --no-legend| sed -e 's/\s\{2,\}/ /g;s/^[[:space:]]*//' | \
+ cut -d ' ' -f 3| grep -v "lightdm"|uniq)"
+
+# Fail if there is no user set
+if [ -z "$x_user" ]; then
+ echo "error no user!"
+ exit 1
+fi
+
+# Export Xorg DISPLAY and XAUTHORITY
+# TODO: Get number of X DISPLAY from tmp
+#declare x_display="/tmp/.X11-unix/X*"
+export DISPLAY=:0
+export XAUTHORITY=/home/$x_user/.Xauthority
+
+# Define the log file
+declare log="/home/$x_user/.cache/$(basename $0)"
+if [[ ! -w "$log" ]]; then
+ touch "$log"
+ chown ${x_user}:${x_user} "$log"
+# exec > "$log" 2>&1
+fi
+
+declare -a arguments=( )
+declare -a connections=( )
+declare -a active_connections=( )
+declare -a available_connections=( )
+declare -a non_primary=( "eDP1" )
+declare -a pan=( "DP2-1" "eDP1" )
+declare -a pan_vertical=( 'empty' 'empty' ) # with empty placeholders!
+declare internal="eDP1"
+declare current_primary=""
+declare action=""
+
+# declare keywords (xrandr arguments)
+declare pre_output=" --output "
+declare post_auto=" --auto "
+declare post_primary=" --primary "
+declare post_pos_rightof=" --right-of"
+declare post_pos_leftof=" --left-of"
+declare post_off=" --off "
+
+function setup_xscreen()
+{
+ local outputs=( )
+ local connection_counter=0
+ case "$action" in
+ "open")
+ if [ ${#available_connections[@]} -gt 1 ]; then
+ for connection in ${pan[@]}
+ do
+ echo "$connection" >> $log
+ set +e
+ find_component_in "$connection" ${available_connections[@]}
+ if [ $? -eq 0 ]; then
+ echo "$connection is amongst the available outputs." >> $log
+ # adding --output argument
+ outputs=( ${outputs[@]:+${outputs[@]}} "$pre_output" )
+ # adding name of output
+ outputs=( ${outputs[@]:+${outputs[@]}} "$connection" )
+ # check whether output should not be primary in a multi output setup
+ find_component_in "$connection" ${non_primary[@]}
+ if [ $? -eq 0 ]; then
+ echo "$connection is setup for non-primary." >> $log
+ else
+ echo "$connection is NOT setup for non-primary." >> $log
+ # adding --primary argument
+ outputs=( ${outputs[@]:+${outputs[@]}} "$post_primary" )
+ fi
+ if [ $connection_counter -gt 0 ]; then
+ # adding a --right-of <connection before this one> argument
+ outputs=( ${outputs[@]:+${outputs[@]}} "$post_pos_rightof" "${pan[$((connection_counter-1))]}" )
+ fi
+ # adding --auto argument
+ outputs=( ${outputs[@]:+${outputs[@]}}"$post_auto" )
+ connection_counter=$(( $connection_counter + 1 ))
+ fi
+ set -e
+ done
+ fi
+ ;;
+ "close")
+ if [ ${#available_connections[@]} -gt 1 ]; then
+ for connection in ${pan[@]}
+ do
+ echo "$connection" >> $log
+ set +e
+ find_component_in "$connection" ${available_connections[@]}
+ if [ $? -eq 0 ]; then
+ echo "$connection is amongst the available outputs." >> $log
+ # adding --output argument
+ outputs=( ${outputs[@]:+${outputs[@]}} "$pre_output" )
+ # adding name of output
+ outputs=( ${outputs[@]:+${outputs[@]}} "$connection" )
+ # if the connection is not the internal output
+ if [ $connection != $internal ]; then
+ # check whether output should not be primary in a multi output setup
+ find_component_in "$connection" ${non_primary[@]}
+ if [ $? -eq 0 ]; then
+ echo "$connection is setup for non-primary." >> $log
+ else
+ echo "$connection is NOT setup for non-primary." >> $log
+ # adding --primary argument
+ outputs=( ${outputs[@]:+${outputs[@]}} "$post_primary" )
+ fi
+ if [ $connection_counter -gt 0 ]; then
+ # adding a --right-of <connection before this one> argument
+ outputs=( ${outputs[@]:+${outputs[@]}} "$post_pos_rightof" "${pan[$((connection_counter-1))]}" )
+ fi
+ # adding --auto argument
+ outputs=( ${outputs[@]:+${outputs[@]}}"$post_auto" )
+ connection_counter=$(( $connection_counter + 1 ))
+ else
+ # adding a --off argument for the interal screen
+ outputs=( ${outputs[@]:+${outputs[@]}} "$post_off" )
+ fi
+ fi
+ set -e
+ done
+ fi
+ ;;
+ esac
+echo "Calling: xrandr ${outputs[@]}" >> $log
+xrandr ${outputs[@]}
+}
+
+function find_component_in()
+{
+ local -r component=$1
+ shift
+ local value
+ for value in "$@"
+ do
+ [[ $value == "$component" ]] && return 0
+ done
+ return 1
+}
+
+function get_current_x_information()
+{
+ # get current xrandr settings
+ current_xrandr=$(xrandr)
+ # get current primary output from xrandr
+ current_primary=$(echo "$current_xrandr" | \
+ grep -wE '(\bconnected.*primary.*[0-9]{1,5}\x[0-9]{1,5}\+[0-9]{1,5}\+[0-9]{1,5})' | \
+ cut -d ' ' -f 1)
+ echo "Current primary output: $current_primary" >> $log
+ # get all current active connections
+ active_connections=( "${active_connections[@]:+${active_connections[@]}}" $(echo "$current_xrandr" | \
+ grep -wE '(\bconnected.*[0-9]{1,5}\x[0-9]{1,5}\+[0-9]{1,5}\+[0-9]{1,5})' | cut -d ' ' -f 1) )
+ echo "Current active connections: ${active_connections[@]}" >> $log
+ # get all currently available connections
+ available_connections=( "${available_connections[@]:+${available_connections[@]}}" $(echo "$current_xrandr" | \
+ grep -wE '(\bconnected.*)' | cut -d ' ' -f 1) )
+ echo "Currently available connections: ${available_connections[@]}" >> $log
+ # get all available connections
+ connections=( "${connections[@]:+${connections[@]}}" $(echo "$current_xrandr"| grep -v 'Screen' | cut -d ' ' -f 1))
+ echo "All current connections: ${connections[@]}" >> $log
+}
+
+function get_action()
+{
+ arguments=( "${arguments[@]:+${arguments[@]}}" "${@:+$@}" )
+# echo "${@:+$@}" >> $log
+# echo "${arguments[@]:+${arguments[@]}}" >> $log
+ if [ ${#arguments[@]} -gt 0 ];then
+ if [ "${arguments[0]}" = "button/lid" ]; then
+ case "${arguments[2]}" in
+ "open")
+ action="open"
+ ;;
+ "close")
+ action="close"
+ ;;
+ esac
+ elif [ "${arguments[0]}" = "login" ]; then
+ action="open"
+ fi
+ fi
+ echo "Action set: $action" >> $log
+}
+
+# pass arguments to the script to the get_action function
+get_action "${@:+$@}"
+get_current_x_information
+setup_xscreen
+
diff --git a/bin/lightdm-display-setup b/bin/lightdm-display-setup
new file mode 100755
index 0000000..94ce0a5
--- /dev/null
+++ b/bin/lightdm-display-setup
@@ -0,0 +1,4 @@
+#!/bin/bash
+# Script to run as lightdm "display-setup-script"
+
+runuser -l dave -c 'autorandr -c'
diff --git a/bin/lowercase b/bin/lowercase
new file mode 100755
index 0000000..09273b0
--- /dev/null
+++ b/bin/lowercase
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# lowerext.sh
+
+while read f; do
+ if [[ "$f" = *.* ]]; then
+ # Extract the basename
+ b="${f%.*}"
+
+ # Extract the extension
+ x="${f##*.}"
+
+ # Convert the extension to lower case
+ # Note: this only works in recent versions of Bash
+ l="${x,,}"
+
+ if [[ "$x" != "$l" ]]; then
+ mv "$f" "$b.$l"
+ fi
+ else
+ continue
+ fi
+done
diff --git a/bin/mirssi b/bin/mirssi
new file mode 100755
index 0000000..6a9ceaa
--- /dev/null
+++ b/bin/mirssi
@@ -0,0 +1,12 @@
+#!/bin/bash
+# start notification daemon
+# connect to server and open port
+# kill notification daemon
+
+PATH=$HOME/bin/:$PATH
+notify_irssi_server.pl &> /dev/null &
+pid=$!
+
+ssh -R 7090:localhost:7090 sleepmap
+
+kill $pid
diff --git a/bin/monitor-hotplug b/bin/monitor-hotplug
new file mode 100755
index 0000000..6c2d1d6
--- /dev/null
+++ b/bin/monitor-hotplug
@@ -0,0 +1,7 @@
+#!/bin/bash
+# Script to be called upon hotplug (change) udev event, to acquire correct resolution for connected screens
+
+export DISPLAY=:0
+export XAUTHORITY=/home/dave/.Xauthority
+
+/usr/bin/autorandr -c &
diff --git a/bin/notify_irssi_server.pl b/bin/notify_irssi_server.pl
new file mode 100755
index 0000000..e1c7b80
--- /dev/null
+++ b/bin/notify_irssi_server.pl
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+#
+use IO::Socket;
+my $sock = new IO::Socket::INET (
+ LocalHost => '127.0.0.1',
+ LocalPort => '7090',
+ Proto => 'tcp',
+ Listen => 1,
+ Reuse => 1,
+);
+die "Could not create socket: $!\n" unless $sock;
+
+while(true) {
+ my $new_sock = $sock->accept();
+ while(<$new_sock>) {
+ my ($server, $channel, $text, $active) = split(/:::/);
+ #print "$_\n";
+ my ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime();
+ # zero padding
+ if ($hour < 10) {
+ $hour = "0$hour";
+ }
+ if ($minute < 10) {
+ $minute = "0$minute";
+ }
+ if ($channel =~ m/#/ ) {
+ $timeout = 10;
+ }
+ else {
+ if ($channel =~ m/bitlbee/ ) {
+ $timeout = 10;
+ }
+ else {
+ $timeout = 5;
+ }
+ }
+
+ ## filter invalid chars
+ # allowed: [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+ $text =~ s/[^\x01-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]//go;
+ # # restricted:[#x1-#x8][#xB-#xC][#xE-#x1F][#x7F-#x84][#x86-#x9F]
+ $text =~ s/[\x01-\x08\x0B-\x0C\x0E-\x1F\x7F-\x84\x86-\x9F]//go;
+
+ ## replace otr information
+ $text =~ s/^03/OTR:\ /;
+
+ ## remove '
+ $text =~ s/\'//g;
+
+ ## remove "
+ $text =~ s/\"//g;
+
+ print "message({action=\"new\", timeout=$timeout, active=$active, title=\"$hour:$minute $channel\", text=\"$text\"})\n";
+ ## AWESOME custom function
+ system("echo 'message({action=\"new\", timeout=$timeout, active=$active, title=\"$hour:$minute $channel\", text=\"$text\"})' | awesome-client");
+ ## AWESOME
+ # system("echo 'notify_irssi=naughty.notify({timeout = $timeout, title = \"$hour:$minute $channel\", text = \"$text\"})' | awesome-client");
+ ## NOTIFY-SEND timeout in ms
+ # system("notify-send -t $timeout*1000 \"$hour:$minute $channel\" \"$text\"");
+ }
+}
+close($sock);
diff --git a/bin/notify_mpd b/bin/notify_mpd
new file mode 100755
index 0000000..8bdcf8c
--- /dev/null
+++ b/bin/notify_mpd
@@ -0,0 +1,42 @@
+#!/bin/bash
+# TODO: notify-send on non availabilty of mpc
+mpc "$@" > /dev/null
+lines=`mpc | wc -l`
+
+if [ $lines -gt 1 ];
+then
+ line1="`mpc | head -n 1`"
+ line2="`mpc | head -n 3 | tail -n 2 | sed 's/[a-z]*\:\ off//g'`"
+
+ status="`echo $line2 | awk '{ print $1 }'`"
+ position="`echo $line2 | awk '{ print $3 }'`"
+ position="($position)"
+ volume="[`echo $line2 | awk '{ print $5 $6 }' | sed 's/volume\://' | sed 's/repeat\://'`]"
+
+ repeat="`echo $line2 | grep -c 'repeat: on' | sed 's/1/r/' | sed 's/0//'`"
+ random="`echo $line2 | grep -c 'random: on' | sed 's/1/z/' | sed 's/0//'`"
+ single="`echo $line2 | grep -c 'single: on' | sed 's/1/s/' | sed 's/0//'`"
+ consume="`echo $line2 | grep -c 'consume: on' | sed 's/1/c/' | sed 's/0//'`"
+ flags="[$repeat$random$single$consume]"
+
+ title="$line1"
+ text="$status $position $flags $volume"
+
+else
+ line="`mpc | sed 's/[a-z]*\:\ off//g'`"
+
+ volume="[`echo $line | awk '{ print $2 }'`]"
+
+ repeat="`echo $line | grep -c 'repeat: on' | sed 's/1/r/' | sed 's/0//'`"
+ random="`echo $line | grep -c 'random: on' | sed 's/1/z/' | sed 's/0//'`"
+ single="`echo $line | grep -c 'single: on' | sed 's/1/s/' | sed 's/0//'`"
+ consume="`echo $line | grep -c 'consume: on' | sed 's/1/c/' | sed 's/0//'`"
+ flags="[$repeat$random$single$consume]"
+
+ title="not playing"
+ text="$flags $volume"
+fi
+
+## NOTIFY-SEND
+notify-send -t 2000 "$title" "$text"
+
diff --git a/bin/nouveau2nvidia b/bin/nouveau2nvidia
new file mode 100755
index 0000000..a71e6b7
--- /dev/null
+++ b/bin/nouveau2nvidia
@@ -0,0 +1,57 @@
+#!/bin/bash
+# nouveau -> nvidia
+
+set -e
+# check if root
+if [[ $EUID -ne 0 ]]; then
+ echo "You must be root to run this script. Aborting...";
+ exit 1;
+fi
+
+echo "Switching MODULES line in /etc/mkinitcpio.conf"
+sed -i 's/MODULES="dm-mod nouveau"/#MODULES="dm-mod nouveau"/' /etc/mkinitcpio.conf
+sed -i 's/#*MODULES="dm-mod nvidia"/MODULES="dm-mod nvidia"/' /etc/mkinitcpio.conf
+
+echo "Uninstalling nouveau drivers, installing nvidia drivers."
+
+declare -i NVIDIAINSTALL
+NVIDIART=$(pacman -Qi|grep Name|grep linux-rt)
+
+if [[ -n "$NVIDIART" ]]; then
+ NVIDIAINSTALL=$NVIDIAINSTALL+1
+ echo "Seems that linux-rt is installed. Marking nvidia-rt for installation."
+fi
+
+pacman -Rdds --noconfirm nouveau-dri xf86-video-nouveau mesa-libgl lib32-nouveau-dri lib32-mesa-libgl
+
+case $NVIDIAINSTALL in
+ 0)
+ pacman -S --noconfirm nvidia lib32-nvidia-libgl
+ ;;
+ 1)
+ pacman -S --noconfirm nvidia lib32-nvidia-libgl
+ aura -A --noconfirm nvidia-rt
+ ;;
+esac
+
+echo "Switching X11 settings."
+NVIDIACONF="/etc/X11/xorg.conf.d/20-nvidia.conf"
+NOUVEAUCONF="/etc/X11/xorg.conf.d/20-nouveau.conf"
+BKP=".bkp"
+
+if [[ -f $NOUVEAUCONF ]]; then
+ mv $NOUVEAUCONF "$NOUVEAUCONF$BKP"
+fi
+
+if [ -f "$NVIDIACONF$BKP" ]; then
+ mv "$NVIDIACONF$BKP" $NVIDIACONF
+fi
+
+
+echo "Building new initramfs images."
+mkinitcpio -p linux
+
+if [[ -f "/etc/mkinitcpio.d/linux-rt.preset" ]]; then
+ mkinitcpio -p linux-rt
+fi
+
diff --git a/bin/nvidia2nouveau b/bin/nvidia2nouveau
new file mode 100755
index 0000000..e862d8c
--- /dev/null
+++ b/bin/nvidia2nouveau
@@ -0,0 +1,58 @@
+#!/bin/bash
+# nvidia -> nouveau
+
+set -e
+# check if root
+if [[ $EUID -ne 0 ]]; then
+ echo "You must be root to run this script. Aborting...";
+ exit 1;
+fi
+
+echo "Switching MODULES line in /etc/mkinitcpio.conf"
+sed -i 's/#*MODULES="dm-mod nouveau"/MODULES="dm-mod nouveau"/' /etc/mkinitcpio.conf
+sed -i 's/MODULES="dm-mod nvidia"/#MODULES="dm-mod nvidia"/' /etc/mkinitcpio.conf
+
+echo "Uninstalling nvidia drivers, installing nouveau drivers."
+
+declare -i NVIDIAINSTALL
+NVIDIART=$(pacman -Qi|grep Name|grep nvidia-rt)
+
+if [[ -n "$NVIDIART" ]]; then
+ NVIDIAINSTALL=$NVIDIAINSTALL+1
+ echo "Seems that nvidia-rt is installed. Marked for removal."
+fi
+
+case $NVIDIAINSTALL in
+ 0)
+ pacman -Rdds --noconfirm nvidia nvidia-libgl lib32-nvidia-libgl
+ ;;
+ 1)
+ pacman -Rdds --noconfirm nvidia nvidia-rt nvidia-libgl lib32-nvidia-libgl
+ ;;
+esac
+
+pacman -S --noconfirm nouveau-dri xf86-video-nouveau lib32-nouveau-dri
+
+echo "Switching X11 settings."
+NVIDIACONF="/etc/X11/xorg.conf.d/20-nvidia.conf"
+NOUVEAUCONF="/etc/X11/xorg.conf.d/20-nouveau.conf"
+BKP=".bkp"
+
+if [ -f $NVIDIACONF ]; then
+ mv $NVIDIACONF "$NVIDIACONF$BKP"
+fi
+
+if [[ -f "$NOUVEAUCONF$BKP" ]]; then
+ mv "$NOUVEAUCONF$BKP" $NOUVEAUCONF
+fi
+
+echo "Building new initramfs images."
+mkinitcpio -p linux
+
+if [[ -f "/etc/mkinitcpio.d/linux-rt.preset" ]]; then
+ mkinitcpio -p linux-rt
+fi
+
+if [[ -f "/etc/mkinitcpio.d/linux-mainline.preset" ]]; then
+ mkinitcpio -p linux-rt
+fi
diff --git a/bin/nvsetup b/bin/nvsetup
new file mode 100755
index 0000000..23adeb5
--- /dev/null
+++ b/bin/nvsetup
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+SCREENCONNECTED=$(xrandr|grep DP-1)
+if [[ -n "$(lsmod|grep nouveau)" ]]; then
+ if [[ "$SCREENCONNECTED" == *connected* && "$SCREENCONNECTED" != *disconnected* ]]; then
+ echo "Setup DP-1"
+# sleep 5
+ xrandr --output DP-1 --auto --primary --output LVDS-1 --auto --right-of DP-1
+ fi
+fi
diff --git a/bin/pacman-disowned b/bin/pacman-disowned
new file mode 100755
index 0000000..df1b5da
--- /dev/null
+++ b/bin/pacman-disowned
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+tmp=${TMPDIR-/tmp}/pacman-disowned-$UID-$$
+db=$tmp/db
+fs=$tmp/fs
+
+mkdir "$tmp"
+trap 'rm -rf "$tmp"' EXIT
+
+pacman -Qlq | sort -u > "$db"
+
+find /bin /etc /lib /sbin /usr \
+ ! -name lost+found \
+ \( -type d -printf '%p/\n' -o -print \) | sort > "$fs"
+
+comm -23 "$fs" "$db"
diff --git a/bin/pacoptpd b/bin/pacoptpd
new file mode 100755
index 0000000..30e09ec
--- /dev/null
+++ b/bin/pacoptpd
@@ -0,0 +1,14 @@
+#!/bin/bash
+#
+# list optional dependencies of installed packages
+#
+
+for i in $(pacman -Qq)
+do
+ deps="$(pacman -Qi ${i} | awk '/Optional/,/Required/' | grep -v "Required" | sed -e 's/Optional Deps[ ]*://g' -e 's/^[ ]*/ /')"
+ if [ "$deps" != " None" ]; then
+ echo "$i"
+ echo "$deps"
+ echo ""
+ fi
+done
diff --git a/bin/pass2msmtp b/bin/pass2msmtp
new file mode 100755
index 0000000..5ba07fa
--- /dev/null
+++ b/bin/pass2msmtp
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+pass Mail/$1
diff --git a/bin/pass2offlineimap b/bin/pass2offlineimap
new file mode 100644
index 0000000..72f5829
--- /dev/null
+++ b/bin/pass2offlineimap
@@ -0,0 +1,5 @@
+#! /usr/bin/env python2
+from subprocess import check_output
+
+def get_pass(account):
+ return check_output("pass Mail/" + account, shell=True).rstrip()
diff --git a/bin/pass2vdirsyncer b/bin/pass2vdirsyncer
new file mode 100755
index 0000000..6b21577
--- /dev/null
+++ b/bin/pass2vdirsyncer
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+pass show owncloud/$1@$2
+
diff --git a/bin/rmcache b/bin/rmcache
new file mode 100755
index 0000000..acba953
--- /dev/null
+++ b/bin/rmcache
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+USERDIR=$1
+
+echo "rmcache cleaning up: $USERDIR"
+
+caches=".cache/chromium"
+caches+=" .cache/thumbnails"
+## take care of the whitespace!
+caches+=" .config/chromium/Default/*Index*"
+caches+=" .thumbnails"
+caches+=" .opera/cache4"
+caches+=" .opera/cache"
+caches+=" .java/deployment/cache"
+caches+=" .local/share/Trash"
+caches+=" .gnome2/epiphany/mozilla/epiphany/Cache"
+caches+=" .adobe/Acrobat/*/Cache"
+caches+=" .adobe/Acrobat/*/Temp"
+caches+=" .macromedia/Flash_Player/*"
+caches+=" .adobe/Flash_Player/AssetCache"
+caches+=" .java/deployment/cache"
+
+pushd $USERDIR > /dev/null
+
+for cache in $caches
+do
+ echo "removing $cache"
+ rm -Rf "$cache"
+ [ $? -eq 0 ] || exit 1
+done
+
+popd > /dev/null
diff --git a/bin/rollback-profile b/bin/rollback-profile
new file mode 100755
index 0000000..81386b3
--- /dev/null
+++ b/bin/rollback-profile
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Rollback a thunderbird or firefox profile backup from store location while gpg decrypting
+#
+
+# Checking if firefox and/ or thunderbird are running.
+firefoxPID=`ps -C firefox -o pid=`
+thunderbirdPID=`ps -C thunderbird -o pid=`
+
+firefoxTMP=/tmp/firefox.tgz
+thunderbirdTMP=/tmp/thunderbird.tgz
+firefoxIF=~/.mozilla/firefox/
+thunderbirdIF=~/.thunderbird/
+profileName=dvzrv
+backupEND=-rollback
+OF=~/ownCloud/backup/web/
+gpgEND=.gpg
+tgzEND=.tgz
+
+case $1 in
+ "thunderbird")
+ if [[ -z "$thunderbirdPID" ]]
+ then
+ echo "Thunderbird is not running."
+ echo "Rolling back profile backup."
+ echo "Copying backup to /tmp and moving old profile to rollback location."
+ cp $OF$1$tgzEND$gpgEND $thunderbirdTMP$gpgEND
+ mv $thunderbirdIF$profileName $thunderbirdIF$profileName$backupEND
+ echo "Decrypting file."
+ gpg -o $thunderbirdTMP -d $thunderbirdTMP$gpgEND
+ echo "Extracting files from tar."
+ tar xzvf $thunderbirdTMP -C $thunderbirdIF
+ echo "Cleaning up."
+ rm $thunderbirdTMP $thunderbirdTMP$gpgEND
+ echo "Done."
+ else
+ echo "Thunderbird is still running."
+ echo "Skipping rollback."
+ fi
+ ;;
+ "firefox")
+ if [[ -z "$firefoxPID" ]]
+ then
+ echo "Firefox is not running."
+ psdStopped=`systemctl status psd |grep inactive`
+ if [[ ! -z "$psdStopped" ]]; then
+ echo "Psd daemon is inactive."
+ echo "Rolling back profile backup."
+ echo "Copying backup to /tmp and moving old profile to rollback location."
+ cp $OF$1$tgzEND$gpgEND $firefoxTMP$gpgEND
+ mv $firefoxIF$profileName $firefoxIF$profileName$backupEND
+ echo "Decrypting file."
+ gpg -o $firefoxTMP -d $firefoxTMP$gpgEND
+ echo "Extracting files from tar."
+ tar xzvf $firefoxTMP -C $firefoxIF
+ echo "Cleaning up."
+ rm $firefoxTMP $firefoxTMP$gpgEND
+ echo "Done."
+ else
+ echo "Psd service is still running!"
+ echo "Disable it using: 'systemctl stop psd'."
+ echo "Skipping rollback."
+ fi
+ else
+ echo "Firefox is still running."
+ echo "Skipping rollback."
+ fi
+ ;;
+esac
diff --git a/bin/run_once b/bin/run_once
new file mode 100755
index 0000000..3829f7e
--- /dev/null
+++ b/bin/run_once
@@ -0,0 +1,4 @@
+#!/bin/bash
+#Alternative
+pgrep $@ > /dev/null || ($@ &)
+
diff --git a/bin/sclang b/bin/sclang
new file mode 100755
index 0000000..a34f4ea
--- /dev/null
+++ b/bin/sclang
@@ -0,0 +1,10 @@
+#!/bin/sh
+# If not running my laptop (with X screen), run sclang in a xvfb environment.
+# This ensures getting around a bug with QPixmaps that needs a X server to run.
+if [[ $HOSTNAME == *dvzrv* ]]; then
+ echo "/usr/bin/sclang"
+ /usr/bin/sclang "$@"
+else
+ echo "/usr/bin/sclang in fake X screen"
+ /usr/bin/xvfb-run -s "-screen 1, 1280x800x24" -a -e ~/.log/xvfb-run-sclang.error /usr/bin/sclang "$@"
+fi
diff --git a/bin/screensetup b/bin/screensetup
new file mode 100755
index 0000000..b15d63e
--- /dev/null
+++ b/bin/screensetup
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+#set dpms and screen blanking
+xset +dpms
+xset s 360 360
+
+# Setup screens
+
+# get info from xrandr
+IFS=$'\r\n' connectedOutputs=($(xrandr | grep " connected" | sed -e "s/\([A-Z0-9]\+\) connected.*/\1/"))
+
+#if more than one screen, setup both, using the external left of the internal as primary
+if [ ${#connectedOutputs[@]} -gt 1 ]; then
+ xrandr --output "${connectedOutputs[0]}" --auto --output "${connectedOutputs[1]}" --auto --primary --left-of "${connectedOutputs[0]}"
+fi
+
+
diff --git a/bin/secret b/bin/secret
new file mode 100755
index 0000000..27391ff
--- /dev/null
+++ b/bin/secret
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Open and write to a gpg encrypted file
+# Will create the file for you if it's not there yet
+#
+# secret <path/to/file>
+
+writeback () {
+ gpg --yes -eq -r "$gpgkey" -o $1 $tmpfile
+ echo "Encrypted file using gpg public key: $gpgkey"
+ chmod 600 $1
+ echo "Finished working on file: $1."
+ rm $tmpfile
+ echo "Removed temp file $tmpfile"
+}
+
+filetotmp () {
+ gpg --yes -o $tmpfile -d $1
+}
+
+tmpfile=$(mktemp)
+gpgkey="David Runge <david.runge@frqrec.com>"
+
+# if file is available, use it, else create it
+if [[ -f "$1" ]];then
+ filetotmp $1
+ echo "File $1 now in $tmpfile ."
+fi
+
+echo "Opening file in $EDITOR"
+# edit decrypted file in /tmp
+$EDITOR $tmpfile
+if [[ $? -gt 0 ]];then
+ echo "Something went wrong with $EDITOR ."
+ echo "Aborting."
+ rm $tmpfile
+ echo "Deleted $tmpfile ."
+else
+ # write back the file
+ writeback $1
+fi
diff --git a/bin/set_backlight b/bin/set_backlight
new file mode 100755
index 0000000..c110321
--- /dev/null
+++ b/bin/set_backlight
@@ -0,0 +1,56 @@
+#!/bin/bash
+# Script to set backlight on startup on Lenovo W540 using
+# /sys/class/backlight/intel_backlight/brightness
+# This must be run as root.
+
+# check if root
+if [[ $EUID -ne 0 ]];then
+ echo "This must be run as root."
+else
+ # get current and maximum brightness, set maximum steps allowed
+ brightness_current=$(cat /sys/class/backlight/intel_backlight/brightness)
+ brightness_max=$(cat /sys/class/backlight/intel_backlight/max_brightness)
+ brightness_max=$(($brightness_max-1000))
+ brightness_steps=20
+ brightness_steps_width=$(($brightness_max/$brightness_steps))
+ brightness_new=0
+ # if first parameter to this script is a number
+ re='^[0-9]+$'
+ if [[ $1 =~ $re ]]; then
+ echo "Direct step called"
+ if [[ $1 -gt brightness_steps && $1 -ne 0 ]];then
+ brightness_new=$(($1*brightness_steps_width))
+ fi
+ elif [[ $1 == "up" || $1 == "down" ]];then
+ brightness_calc=$(($brightness_current / $brightness_steps_width))
+ case $1 in
+ "up" )
+ brightness_calc=$((($brightness_calc+1) * $brightness_steps_width))
+ echo "brightness_calc: $brightness_calc"
+ echo "brightness_max: $brightness_max"
+ if [[ $brightness_max -gt $brightness_calc ]];then
+ echo "true"
+ brightness_new=$brightness_calc
+ else
+ echo "false"
+ brightness_new=$brightness_current
+ fi
+ ;;
+ "down" )
+ brightness_calc=$((($brightness_calc-1) * $brightness_steps_width))
+ echo "brightness_calc: $brightness_calc"
+ if [[ $brightness_calc -gt 0 ]];then
+ brightness_new=$brightness_calc
+ else
+ brightness_new=$brightness_current
+ fi
+ ;;
+ esac
+ echo "Step called with: $1"
+ fi
+ echo $brightness_current
+ echo $brightness_new
+ # set to 234 (around 4th step)
+ #echo 234 > /sys/class/backlight/intel_backlight/brightness
+ echo $brightness_new > /sys/class/backlight/intel_backlight/brightness
+fi
diff --git a/bin/set_volume b/bin/set_volume
new file mode 100755
index 0000000..1652312
--- /dev/null
+++ b/bin/set_volume
@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+
+FUNCTIONS=$HOME/bin/functions.sh
+[ -e $FUNCTIONS ] || exit 1
+. $FUNCTIONS
+
+state_muted="/tmp/$(whoami)/state_muted"
+state_muted_headphone=""
+state_muted_speaker=""
+state_bt_headphone=""
+name_bt_headphone="bluez_sink.00_1B_66_02_36_41"
+cardselector=""
+card_icon="/usr/share/icons/gnome/48x48/devices/audio-speakers.png" # gnome-icon-theme
+
+function increase_volume()
+{
+ mute amixer $cardselector sset Master 5%+
+ local state_master=$(amixer $cardselector sget Master |grep "%" | cut -d'%' -f1 | cut -d '[' -f2 | uniq)
+ #update pactl bluetooth audio to same level if present
+ if [ $state_bt_headphone = "[on]" ];then
+ pactl set-sink-volume $name_bt_headphone "$state_master%"
+ fi
+ print_volumes $state_master
+}
+
+function decrease_volume()
+{
+ mute amixer $cardselector sset Master 5%-
+ local state_master=$(amixer $cardselector sget Master |grep "%" | cut -d'%' -f1 | cut -d '[' -f2 | uniq)
+ #update pactl bluetooth audio to same level if present
+ if [ $state_bt_headphone = "[on]" ];then
+ pactl set-sink-volume $name_bt_headphone "$state_master%"
+ fi
+ print_volumes $state_master
+}
+
+function print_volumes()
+{
+ local state_master=$1
+ local state_headphone=$(amixer $cardselector sget Headphone |grep "%" | cut -d'%' -f1 | cut -d '[' -f2 | uniq)
+ local state_speaker=$(amixer $cardselector sget Speaker |grep "%" | cut -d'%' -f1 | cut -d '[' -f2 | uniq)
+ if [ $state_bt_headphone = "[on]" ]; then
+ send_notify "Master: $state_master% \nHeadphone: $state_headphone% \nSpeaker: $state_speaker%\nBT Headphone: $state_master%"
+ else
+ send_notify "Master: $state_master% \nHeadphone: $state_headphone% \nSpeaker: $state_speaker%"
+ fi
+}
+
+function toggle_volume()
+{
+ local state_master=$(amixer $cardselector sget Master | grep -o '\[o[n|f]*\]' | head -n 1)
+ local state_headphone=$(amixer $cardselector sget Headphone | grep -o '\[o[n|f]*\]' | head -n 1)
+ local state_speaker=$(amixer $cardselector sget Speaker | grep -o '\[o[n|f]*\]' | head -n 1)
+ case "$state_master" in
+ "[on]")
+ mute amixer $cardselector sset Master mute
+ state_master="[off]"
+ # if headphones are off already, don't mute, instead save state to tmp file
+ if [ "$state_headphone" = "[off]" ];then
+ echo "headphone [off]" >> "$state_muted"
+ else
+ mute amixer $cardselector sset Headphone mute
+ state_headphone="[off]"
+ fi
+ # if speakers are off already, don't mute, instead save state to tmp file
+ if [ "$state_speaker" = "[off]" ];then
+ echo "speaker [off]" >> "$state_muted"
+ else
+ mute amixer $cardselector sset Speaker mute
+ state_speaker="[off]"
+ fi
+ # if present, also mute bluetooth headphone
+ if [ $state_bt_headphone = "[on]" ]; then
+ pactl set-sink-mute $name_bt_headphone 1
+ fi
+ ;;
+ "[off]")
+ mute amixer $cardselector sset Master unmute
+ state_master="[on]"
+ # if headphones are meant to be off, don't unmute them again
+ if [ "$state_muted_headphone" != "[off]" ];then
+ mute amixer $cardselector sset Headphone unmute
+ state_headphone="[on]"
+ fi
+ # if headphones are meant to be off, don't unmute them again
+ if [ "$state_muted_speaker" != "[off]" ];then
+ mute amixer $cardselector sset Speaker unmute
+ state_speaker="[on]"
+ fi
+ # if present, also unmute bluetooth headphone
+ if [ $state_bt_headphone = "[on]" ]; then
+ pactl set-sink-mute $name_bt_headphone 0
+ fi
+ ;;
+ esac
+ if [ $state_bt_headphone = "[on]" ]; then
+ send_notify "Master: $state_master \nHeadphone: $state_headphone \nSpeaker: $state_speaker\nBT Headphone: $state_master" 2000
+ else
+ send_notify "Master: $state_master \nHeadphone: $state_headphone \nSpeaker: $state_speaker" 2000
+ fi
+}
+
+function send_notify()
+{
+ # allow timeout to be set by 2nd argument
+ local timeout=500
+ if [ $2 -gt 0 ]; then
+ timeout=$2
+ fi
+ notify-send -t $timeout \
+ -i /usr/share/icons/gnome/48x48/devices/audio-speakers.png \
+ "Volume" "$1"
+}
+
+if [ $HOSTNAME = "dvzrv" ];then
+ cardselector=" -M -c 1 "
+ if [ -n "$(pactl list sinks short | grep $name_bt_headphone)" ]; then
+ state_bt_headphone="[on]"
+ fi
+ #TODO: also check pactl for JACK sink
+fi
+
+case "$1" in
+ increase)
+ increase_volume
+ ;;
+ decrease)
+ decrease_volume
+ ;;
+ toggle)
+ if [ -f "$state_muted" ]; then
+ state_muted_headphone=$(cat "$state_muted" | grep headphone | cut -d' ' -f2)
+ state_muted_speaker=$(cat "$state_muted" | grep speaker | cut -d' ' -f2)
+ rm "$state_muted"
+ else
+ touch "$state_muted"
+ fi
+ toggle_volume
+ ;;
+ *)
+ ;;
+esac
diff --git a/bin/stikked b/bin/stikked
new file mode 100644
index 0000000..b209208
--- /dev/null
+++ b/bin/stikked
@@ -0,0 +1,115 @@
+#! /usr/bin/env python2
+
+import argparse
+import os
+import sys
+import pycurl
+import StringIO
+import subprocess
+import urllib
+
+## CONFIGURATION {
+user='example-user'
+apiurl='http://paste.giev.de/api/create'
+## }
+
+
+def main():
+ global verbose
+
+ # parse arguments
+ parser = argparse.ArgumentParser(prog='stikked')
+ parser.add_argument('-V', '--version', action='version', version='%(prog)s 0.1')
+ parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help='verbose mode', default=False)
+ parser.add_argument('-s', dest='syntax', action='store', help='syntax to highlight', default='text')
+ parser.add_argument('-p', dest='private', action='store_true', help='set paste to private', default=False)
+ parser.add_argument('-t', dest='title', action='store', help='set title of paste')
+ parser.add_argument('fname', metavar='FILE|-', help='file to paste or - for stdin')
+ args = parser.parse_args()
+ if args.verbose:
+ verbose = args.verbose
+ print args
+
+ # private
+ private = '1' if args.private else '0'
+
+ # from stdin
+ if args.fname == '-':
+ title = args.title if args.title else 'stdin'
+ text=sys.stdin.read()
+ syntax=args.syntax
+
+ # from file
+ else:
+ fname = args.fname
+
+ if not os.path.isfile(fname):
+ print "No such file: {0}".format(fname)
+ sys.exit(1)
+ else:
+ title = args.title if args.title else os.path.split(fname)[1]
+
+ if args.syntax == 'text':
+ ext = os.path.splitext(fname)[1][1:]
+ syntax = _detect_syntax(ext)
+ else:
+ syntax = args.syntax
+
+ fopen = file(fname, 'r')
+ text = fopen.read()
+
+ _paste(text, title, syntax, private)
+
+
+# send paste to server
+def _paste(text, title, syntax, private):
+ postparams = [
+ ('text', text),
+ ('name', user),
+ ('title', title),
+ ('lang', syntax),
+ ('private', private)]
+
+ postfields=urllib.urlencode(postparams)
+
+ curl = pycurl.Curl()
+ curl.setopt(pycurl.URL, apiurl)
+ curl.setopt(pycurl.POST, True)
+ curl.setopt(pycurl.POSTFIELDS, postfields)
+ # Fixes the HTTP/1.1 417 Expectation Failed Bug
+ header = []
+ header.append("Expect: ")
+ curl.setopt(pycurl.HTTPHEADER, header)
+ curl.setopt(pycurl.NOPROGRESS, True)
+ b = StringIO.StringIO()
+ curl.setopt(pycurl.WRITEFUNCTION, b.write)
+ curl.perform()
+ print b.getvalue();
+
+ # copy to primary clipboard
+ xsel_proc = xsel_proc = subprocess.Popen(['xsel', '-pi'], stdin=subprocess.PIPE)
+ xsel_proc.communicate(b.getvalue())
+
+
+# more possible of course
+def _detect_syntax(ext):
+ if ext == 'h':
+ syntax='c'
+ elif ext == 'log':
+ syntax='logcat'
+ elif ext == "py":
+ syntax = 'python'
+ elif ext == "pl":
+ syntax = 'perl'
+ elif ext == "patch":
+ syntax = 'diff'
+ elif ext == 'c' or ext == 'cpp' or ext == 'java' or ext == 'sh':
+ syntax=ext
+ else:
+ syntax='text'
+
+ return syntax
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/stop_jack b/bin/stop_jack
new file mode 100755
index 0000000..f07b6be
--- /dev/null
+++ b/bin/stop_jack
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+jack_control stop
+sleep 3
+jack_control exit
+
+
+exit 0
+
+#kill $(pidof jackd)
+#case "$HOSTNAME" in
+# "beagleclone")
+# kill $(pidof jackd)
+# kill $(pidof xvfb-run)
+# ;;
+#esac
diff --git a/bin/switch_wfs_os b/bin/switch_wfs_os
new file mode 100755
index 0000000..c243826
--- /dev/null
+++ b/bin/switch_wfs_os
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+IFS=$'\n\t'
+
+os_new="arch-rt"
+os_old="ubuntu-studio"
+os_current=""
+os_set=""
+
+syslinux_location_arch="/boot/syslinux/syslinux.cfg"
+syslinux_location_ubuntu="/mnt/boot/syslinux/syslinux.cfg"
+syslinux_location=""
+
+reboot_cmd_arch="sudo systemctl reboot"
+reboot_cmd_ubuntu="/usr/bin/sudo /sbin/reboot -q"
+reboot_cmd=""
+
+hostnames_arch=( "wfs-master" "wfs-node1" "wfs-node2" )
+hostnames_ubuntu=( "wfs" "n101" "n102" )
+host=$(hostname)
+
+if [ $UID -ne 0 ]; then
+ echo "You need to be root or call this script with sudo to do this."
+ exit 1
+fi
+
+echo "Host: $host."
+
+if [ -f /etc/arch-release ]; then
+ os_current=$os_new
+ syslinux_location=$syslinux_location_arch
+ reboot_cmd=$reboot_cmd_arch
+else
+ os_current=$os_old
+ syslinux_location=$syslinux_location_ubuntu
+ reboot_cmd=$reboot_cmd_ubuntu
+fi
+echo "Currently running: $os_current"
+
+if [ -f "${syslinux_location}" ]; then
+ os_set=$(sed -ne '/^DEFAULT/p' $syslinux_location| cut -d' ' -f2)
+ echo "Currently set up for next boot: $os_set"
+else
+ echo "Error: Syslinux configuration does not exist ($syslinux_location)"
+ exit 1
+fi
+
+if [ "$os_set" == "${os_new}" ] && [ "$os_current" == "${os_new}" ]; then
+ if [[ " ${hostnames_arch[@]} " =~ " $host " ]]; then
+ echo "Switching to: $os_old"
+ sed -i 's/^DEFAULT .*/DEFAULT '${os_old}'/' $syslinux_location
+ if [[ "${hostnames_arch[0]}" == "$host" ]]; then
+ echo "Calling script on nodes:"
+ set +e
+ ssh wfs@${hostnames_arch[1]} "/usr/bin/sudo bin/$(basename $0)"
+ ssh wfs@${hostnames_arch[2]} "/usr/bin/sudo bin/$(basename $0)"
+ set -e
+ fi
+ else
+ echo "Error: Script is not supposed to be running on this host!"
+ echo "$(hostname) is not one of the following: ${hostnames_arch[@]}"
+ echo "Exiting."
+ exit 1
+ fi
+elif [ "$os_set" == "${os_old}" ] && [ "$os_current" == "${os_old}" ]; then
+ if [[ " ${hostnames_ubuntu[@]} " =~ " $host " ]]; then
+ echo "Switching to: $os_new"
+ sed -i 's/^DEFAULT .*/DEFAULT '${os_new}'/' $syslinux_location
+ if [[ "${hostnames_ubuntu[0]}" == "$host" ]]; then
+ echo "Calling script on nodes:"
+ ssh wfs@${hostnames_ubuntu[1]} "/usr/bin/sudo bin/$(basename $0)"
+ ssh wfs@${hostnames_ubuntu[2]} "/usr/bin/sudo bin/$(basename $0)"
+ fi
+ else
+ echo "Error: Script is not supposed to be running on this host!"
+ echo "$host is not one of the following: ${hostnames_ubuntu[@]}"
+ echo "Exiting."
+ exit 1
+ fi
+else
+ echo "Error: Current OS ($os_current) and OS to be booted ($os_set) are not the same."
+ echo "Fix this manually in $syslinux_location or just reboot (into the other OS)."
+ echo "Exiting."
+ exit 1
+fi
+
+
+echo "Rebooting."
+eval $reboot_cmd
+exit 0
diff --git a/bin/syncmpdpl b/bin/syncmpdpl
new file mode 100644
index 0000000..85cf723
--- /dev/null
+++ b/bin/syncmpdpl
@@ -0,0 +1,209 @@
+#! /usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+# script to sync a mpd playlist to a mass storage audio player
+#
+# needs
+# - python-sqlite2
+# - eyed3
+# - python-argparser
+# - python-mpd
+
+import argparse
+import os
+import sys
+import hashlib
+import subprocess
+
+#from time import time
+import time
+from mpd import (MPDClient, CommandError)
+
+import shutil
+
+# set encoding to utf-8
+reload(sys)
+sys.setdefaultencoding( "utf-8" )
+
+config_dir = '/home/konni/.config/pmsync'
+music = '/net/media/music'
+playlist = 'sync'
+mpdhost = 'innocence'
+destination = '/run/media/konni/ville/music'
+verbose = False
+
+def _recode(string):
+ string = string.replace('"','')
+ # byte2utf8
+ # string = unicode( string, "utf-8", errors="ignore" )
+ # utf2byte
+ string = string.encode('utf-8')
+ return string
+
+def main():
+ global verbose, playlist, mpdhost, destination
+ parser = argparse.ArgumentParser(prog='pmsync')
+
+ parser.add_argument('--version', action='version', version='%(prog)s 0.1')
+ parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help='verbose mode', default=False)
+
+ parser.add_argument('action', metavar='sync | list | import')
+
+ parser.add_argument('-m', dest='host', metavar='mpdhost', action='store', help='mpd host', default=mpdhost)
+ parser.add_argument('-p', dest='pl', metavar='playlist', action='store', help='playlist to use', default=playlist)
+ parser.add_argument('-d', dest='dest', metavar='destination', action='store', help='destination', default=destination)
+ parser.add_argument('-e', dest='exact', action='store_true', help='exact checking (slow)', default=False)
+
+ args = parser.parse_args()
+ if args.verbose:
+ verbose = args.verbose
+ print args
+
+ ## action sync
+ if args.action == 'sync':
+ print '>> syncing \"{0}/{1}\"to \"{2}\"'.format(args.host, args.pl, args.dest)
+ _sync(args.host, args.pl, args.dest, args.exact)
+
+ ## action list
+ elif args.action == 'list':
+ print '>> listing from \"{0}/{1}\"'.format(args.host, args.pl)
+ _list(args.host, args.pl)
+
+ ## action import
+ elif args.action == 'import':
+ print '>> importing from \"{0}\" to playlist \"{1}/{2}\"'.format(args.dest, args.host, args.pl)
+ port='6600'
+ CON_ID = {'host':args.host, 'port':port}
+ client = MPDClient()
+ mpdConnect(client, CON_ID)
+ playlistinfo = client.listplaylistinfo(playlist)
+ for dirname, dirnames, filenames in os.walk(args.dest):
+ for filename in filenames:
+ destfile = os.path.join(dirname, filename)
+ path=destfile.replace(args.dest+"/","")
+ found = False
+ for item in playlistinfo:
+ if item['file'] == path:
+ found = True
+ if not found:
+ print '[x] {0}'.format(path)
+ client.add(path)
+ client.rm(args.pl)
+ client.save(args.pl)
+
+ ## show help if no action called
+ else:
+ parser.print_help()
+
+def _sync(host, pl, dest, exact):
+ _checkdir(dest)
+ port='6600'
+ CON_ID = {'host':host, 'port':port}
+ client = MPDClient()
+ mpdConnect(client, CON_ID)
+ playlistinfo = client.listplaylistinfo(pl)
+
+ ## copy files
+ print '> copy files'
+ for item in playlistinfo:
+ path = item['file']
+ srcfile=os.path.join(music, path)
+ destfile=os.path.join(dest, path)
+ destdir=os.path.dirname(destfile)
+
+ if not os.path.isdir(destdir):
+ print '[^] {0}'.format(destdir)
+ os.makedirs(destdir)
+ if not os.path.isfile(destfile):
+ shutil.copyfile(srcfile,destfile)
+ print '[+] {0}'.format(path)
+ else:
+ ## hash based on md5sum or just file size
+ if exact:
+ srchash = hashlib.md5(open(srcfile, 'rb').read()).hexdigest()
+ desthash = hashlib.md5(open(destfile, 'rb').read()).hexdigest()
+ else:
+ srchash = os.path.getsize(srcfile)
+ desthash = os.path.getsize(destfile)
+
+ if srchash != desthash:
+ shutil.copyfile(srcfile,destfile)
+ print '[o] {0}'.format(path)
+ else:
+ if verbose:
+ print '[ ] {0}'.format(path)
+
+ print 'done'
+
+ ## clean files
+ print '> clean files and directories'
+ for dirname, dirnames, filenames in os.walk(dest):
+ for filename in filenames:
+ destfile = os.path.join(dirname, filename)
+ path=destfile.replace(dest+"/","")
+ found = False
+ for item in playlistinfo:
+ if item['file'] == path:
+ found = True;
+
+ if not found:
+ try:
+ os.remove(destfile)
+ print '[-] {0}'.format(path)
+ except OSError:
+ print 'error removing {0}'.format(destfile)
+
+ ## clean directories
+ for dirname, dirnames, filenames in os.walk(dest,topdown=False):
+ if os.listdir(dirname) == [] and dirname != dest:
+ try:
+ os.rmdir(dirname)
+ print '[v] {0}'.format(destdir)
+ except OSError:
+ print 'error removing {0} '.format(dirname)
+ print 'done'
+
+ print '> writing filesystem changes'
+ subprocess.call("/bin/sync")
+
+
+def _list(host, pl):
+ port='6600'
+ CON_ID = {'host':host, 'port':port}
+ client = MPDClient()
+ mpdConnect(client, CON_ID)
+ playlistinfo = client.listplaylistinfo(pl)
+
+ ## output
+ print '> copy files'
+ prev_album = ''
+ for item in playlistinfo:
+ artist = item['artist']
+ album = item['album']
+
+ if album != prev_album:
+ print '{0}/{1}'.format(artist, album)
+ prev_album = album
+
+def _output(artist, album, track, title, symbol='', extra=''):
+ output_format = '{symbol:4}{artist:20.20} :: {album:20.20} :: {track:2d} - {title:25.25} {extra}'
+ print output_format.format(symbol=symbol, artist=artist, album=album, track=track, title=title, extra=extra)
+
+
+def mpdConnect(client, con_id):
+ """
+Simple wrapper to connect MPD.
+"""
+ try:
+ client.connect(**con_id)
+ except SocketError:
+ return False
+ return True
+
+def _checkdir(dir):
+ if not os.path.isdir(dir):
+ print "destination not mounted"
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/tex2pdf b/bin/tex2pdf
new file mode 100755
index 0000000..71718f0
--- /dev/null
+++ b/bin/tex2pdf
@@ -0,0 +1,33 @@
+#!/bin/bash
+logdestination="$HOME/.log/tex2pdf.log"
+already_open=$(ps aux | grep "$1.pdf" | grep -v "grep")
+rm -rf $logdestination
+touch $logdestination
+echo "tex2pdf called with $1.tex">>$logdestination
+echo "#####################################################">>$logdestination
+echo "################## pdflatex, run #1 #################">>$logdestination
+echo "#####################################################">>$logdestination
+pdflatex -interaction=nonstopmode $1.tex>>$logdestination
+echo "bibtex:">>$logdestination
+bibtex $1.aux>>$logdestination
+echo "#####################################################">>$logdestination
+echo "################## pdflatex, run #2 #################">>$logdestination
+echo "#####################################################">>$logdestination
+pdflatex -interaction=nonstopmode $1.tex>>$logdestination
+echo "####### ##############################################">>$logdestination
+echo "################## pdflatex, run #3 #################">>$logdestination
+echo "#####################################################">>$logdestination
+pdflatex -interaction=nonstopmode $1.tex>>$logdestination
+echo "#####################################################">>$logdestination
+echo "Launching mupdf with $1.pdf.">>$logdestination
+
+if [ -n "$already_open" ]; then
+ kill "$(echo $already_open | awk '{print $2}')"
+ echo "Killing pid $(echo $already_open | awk '{print $2}'), which has $1.pdf open already." >>$logdestination
+fi
+mupdf $1.pdf &
+
+echo "#####################################################">>$logdestination
+echo "Removing aux,bbl,blg,dvi,lof,log,lot,nav,out,snm,toc.">>$logdestination
+echo "Current directory: $(pwd) and children of depth 1.">>$logdestination
+rm -f *.{aux,bbl,blg,dvi,lof,log,lot,nav,out,snm,toc} */*.{aux,bbl,blg,dvi,lof,log,lot,nav,out,snm,toc}
diff --git a/bin/texcount.pl b/bin/texcount.pl
new file mode 100755
index 0000000..1f47025
--- /dev/null
+++ b/bin/texcount.pl
@@ -0,0 +1,1391 @@
+#! /usr/bin/env perl
+use strict;
+use warnings;
+use Term::ANSIColor;
+use Encode;
+use POSIX qw(locale_h);
+use locale;
+setlocale(LC_CTYPE,"no_NO");
+
+my $versionnumber="2.2";
+my $versiondate="2009 Apr 30";
+
+###### Set CMD specific settings and variables
+
+# Options and states
+my $verbose=0;
+my $showcodes=1;
+my $showstates=0;
+my $showsubcounts=0;
+my $htmlstyle=0;
+my $includeTeX=0;
+my $briefsum=0;
+my @sumweights;
+my $utf8flag=0;
+
+# Global variables
+my $blankline=0;
+my $errorcount=0;
+
+# CMD specific global variables
+my $totalflag=0;
+my @filelist;
+my $workdir;
+my $globalworkdir="";
+
+###### Set global settings and variables
+
+### Macros for headers
+# Macros that identify headers: i.e. following token or
+# {...} is counted as header. The =>[2] indicates transition to
+# state 2 which is used within headers (although the value is
+# actually never used). This is copied to %TeXmacro and the
+# only role of defining it here is that the counter for the number
+# of headers is incremented by one.
+my %TeXheader=('\title'=>[2],'\part'=>[2],'\chapter'=>[2],
+ '\section'=>[2],'\subsection'=>[2],'\subsubsection'=>[2],
+ '\paragraph'=>[2],'\subparagraph'=>[2]);
+
+### How many tokens to gobble after macro
+# Each macro is assumed to gobble up a given number of
+# tokens (or {...} groups), as well as options [...] before, within
+# and after. The %TeXmacro hash gives a link from a macro
+# (or beginNAME for begin-end groups without the backslash)
+# to either an integer giving the number of tokens to ignore
+# or to an array (specified as [num,num,...]) of length N where
+# N is the number of tokens to be read with the macro and the
+# array values tell how each is to be interpreted (see the status
+# values: 0=ignore, 1=count, etc.). Thus specifying a number N is
+# equivalent to specifying an array [0,...,0] of N zeros.
+#
+# For macros not specified here, the default value is 0: i.e.
+# no tokens are excluded, but [...] options are. Header macros
+# specified in %TeXheader are automatically included here.
+my %TeXmacro=(%TeXheader,
+ '\documentclass'=>1,'\documentstyle'=>1,'\usepackage'=>1, '\hyphenation'=>1,
+ '\pagestyle'=>1,'\thispagestyle'=>1, '\pagenumbering'=>1,'\markboth'=>1, '\markright'=>1,
+ '\newcommand'=>[-3,-3],'\renewcommand'=>[-3,-3],
+ '\newenvironment'=>[-3,-3,-3], 'renewenvironment'=>[-3,-3,-3],
+ '\newfont'=>2,'\newtheorem'=>2,'\bibliographystyle'=>1, '\bibliography'=>1,
+ '\parbox'=>1, '\marginpar'=>[3],'\makebox'=>0, '\raisebox'=>1, '\framebox'=>0,
+ '\newsavebox'=>1, '\sbox'=>1, '\savebox'=>2, '\usebox'=>1,'\rule'=>2,
+ '\footnote'=>[3],'\label'=>1, '\ref'=>1, '\pageref'=>1, '\bibitem'=>1,
+ '\cite'=>1, '\citep'=>1, '\citet'=>1, '\citeauthor'=>1, '\citealt'=>1, '\nocite'=>1,
+ '\eqlabel'=>1, '\eqref'=>1,'\hspace'=>1, '\vspace'=>1, '\addvspace'=>1,
+ '\input'=>1, '\include'=>1, '\includeonly'=>1,'\includegraphics'=>1,
+ '\newlength'=>1, '\setlength'=>2, '\addtolength'=>2,'\settodepth'=>2,
+ '\settoheight'=>2, '\settowidth'=>2,'\newcounter'=>1, '\setcounter'=>2,
+ '\addtocounter'=>2,'\stepcounter'=>1, '\refstepcounter'=>1, '\usecounter'=>1,
+ '\alph'=>1, '\arabic'=>1, '\fnsymbol'=>1, '\roman'=>1, '\value'=>1,
+ '\cline'=>1, '\multicolumn'=>3,'\typeout'=>1, '\typein'=>1,
+ 'beginlist'=>2, 'beginminipage'=>1, 'begintabular'=>1,
+ 'beginthebibliography'=>1,'beginlrbox'=>1,
+ '\begin'=>1,'\end'=>1,'\title'=>[2]);
+
+### Macros that should be counted as one or more words
+# Macros that represent text may be declared here. The value gives
+# the number of words the macro represents.
+my %TeXmacroword=('\LaTeX'=>1,'\TeX'=>1);
+
+### Macros that are counted within the preamble
+# The preamble is the text between \documentclass and \begin{document}.
+# Text and macros in the preamble is ignored unless specified here. The
+# value is the status (1=text, 2=header, etc.) they should be interpreted as.
+# Note that only the first unit (token or {...} block) is counted.
+my %TeXpreamble=('\title'=>[2],
+ '\newcommand'=>[-3,-3],'\renewcommand'=>[-3,-3],
+ '\newenvironment'=>[-3,-3,-3], 'renewenvironment'=>[-3,-3,-3],
+ );
+
+### Begin-End groups
+# Identified as begin-end groups, and define =>state. The
+# states used corresponds to the elements of the count array, and
+# are:
+# 0: Not included
+# 1: Text, word included in text count
+# 2: Header, words included in header count
+# 3: Float caption, words included in float caption count
+# 6: Inline mathematics, words not counted
+# 7: Displayed mathematics, words not counted
+# -1: Float, not included, but looks for captions
+#
+# 4 and 5 are used to count number of headers and floats
+# and are not used as states.
+#
+# Groups that are not defined will be counted as the surrounding text.
+#
+# Note that some environments may only exist within math-mode, and
+# therefore need not be defined here: in fact, they should not as it
+# is not clear if they will be in inlined or displayed math.
+#
+my %TeXgroup=('document'=>1,'letter'=>1,'titlepage'=>0,
+ 'center'=>1,'flushleft'=>1,'flushright'=>1,
+ 'abstract'=>1,'quote'=>1,'quotation'=>1,'verse'=>1,'minipage'=>1,'verbatim'=>1,
+ 'description'=>1,'enumerate'=>1,'itemize'=>1,'list'=>1,
+ 'theorem'=>1,'lemma'=>1,'definition'=>1,'corollary'=>1,'example'=>1,
+ 'math'=>6,'displaymath'=>7,'equation'=>7,'eqnarray'=>7,'align'=>7,
+ 'figure'=>-1,'float'=>-1,'picture'=>-1,'table'=>-1,
+ 'tabbing'=>0,'tabular'=>0,'thebibliography'=>0,'lrbox'=>0);
+
+### In floats: include only specific macros
+# Macros used to identify caption text within floats.
+my %TeXfloatinc=('\caption'=>[3]);
+
+### Macros for including tex files
+# Allows \macro{file} or \macro file. If the value is 0, the filename will
+# be used as is; if it is 1, the filetype .tex will be added if the
+# filename is without filetype; if it is 2, the filetype .tex will be added.
+my %TeXfileinclude=('\input'=>1,'\include'=>2);
+
+### Count labels
+# Labels used to describe the counts
+my @countlabel=('Files','Words in text','Words in headers',
+ 'Words in float captions','Number of headers','Number of floats',
+ 'Number of math inlines','Number of math displayed');
+
+### Break points
+# Definition of macros that define break points that start a new subcount.
+# The values given are used as labels.
+my %BreakPointsOptions;
+$BreakPointsOptions{'none'}={};
+$BreakPointsOptions{'part'}={%{$BreakPointsOptions{'none'}},'\part'=>'Part'};
+$BreakPointsOptions{'chapter'}={%{$BreakPointsOptions{'part'}},'\chapter'=>'Chapter'};
+$BreakPointsOptions{'section'}={%{$BreakPointsOptions{'chapter'}},'\section'=>'Section'};
+$BreakPointsOptions{'subsection'}={%{$BreakPointsOptions{'section'}},'\subsection'=>'Subsection'};
+$BreakPointsOptions{'default'}=$BreakPointsOptions{'subsection'};
+my %BreakPoints=%{$BreakPointsOptions{'none'}};
+
+### Print styles
+# Definition of different print styles: maps of class labels
+# to ANSI codes. Class labels are as used by HTML styles.
+my @STYLES=();
+my %STYLE;
+$STYLES[0]={'error'=>'bold red'};
+$STYLES[1]={%{$STYLES[0]},
+ 'word1'=>'blue','word2'=>'bold blue','word3'=>'blue',
+ 'grouping'=>'red','document'=>'red','mathgroup'=>'magenta',
+ 'state'=>'cyan underline','sumcount'=>'yellow'};
+$STYLES[2]={%{$STYLES[1]},
+ 'command'=>'green','exclcommand'=>'yellow','exclgroup'=>'yellow','exclmath'=>'yellow',
+ 'ignore'=>'cyan'};
+$STYLES[3]={%{$STYLES[2]},
+ 'tc'=>'bold yellow','comment'=>'yellow','option'=>'yellow',
+ 'fileinclude'=>'bold green'};
+$STYLES[4]={%{$STYLES[3]}};
+
+### Word regexp pattern list
+# List of regexp patterns that should be analysed as words.
+# Use @ to represent a letter, will be substituted with $LetterPattern.
+my @WordPatterns=('(@+\.)+@+\.?','@+([\-\']@+)*');
+my $specialchars='\\\\(ae|AE|o|O|aa|AA)';
+my $modifiedchars='\\\\[\'\"\`\~\^\=](\w|\{\w\})';
+my $LetterPattern='\w';
+my $LetterPatternRelaxed='([\w\-\']|'.$modifiedchars.'|'.$specialchars.'(\{\})?|\{'.$specialchars.'\}|\{\w\})';
+my %NamedWordPattern;
+$NamedWordPattern{'chinese'}='\p{script=Han}';
+$NamedWordPattern{'japanese'}='(\p{script=Han}|\p{script=Hiragana}|\p{script=Katakana})';
+
+### Macro option regexp list
+# List of regexp patterns to be gobbled as macro option in and after
+# a macro.
+my @MacroOptionPatterns=('\[(\w|[,\-\s\~\.\:\;\+\?\*\_\=])*\]');
+my @MacroOptionPatternsRelaxed=('\[[^\[\]\n]*\]');
+
+###### Main script
+
+
+###################################################
+
+MAIN(@ARGV);
+
+###################################################
+
+
+#########
+######### Main routines
+#########
+
+# MAIN ROUTINE: Handle arguments, then parse files
+sub MAIN {
+ my @args=@_;
+ my @toplevelfiles=Parse_Arguments(@args);
+ Apply_Options();
+ if (scalar(@toplevelfiles)==0) {
+ conditional_print_help_style()
+ || print_error("No files specified.","p","error");
+ } else {
+ conditional_print_help_style();
+ my $totalcount=parse_file_list(@toplevelfiles);
+ conditional_print_total($totalcount);
+ }
+ Report_ErrorCount();
+ Close_Output();
+}
+
+# Checks arguments, exits on exit condition
+sub Check_Arguments {
+ my @args=@_;
+ if (!@args) {
+ print_version();
+ print_syntax();
+ print_reference();
+ exit;
+ } elsif ($args[0]=~/^(\-?\-(h|\?|help)|\/(\?|h))$/) {
+ print_help();
+ exit;
+ } elsif ($args[0]=~/^\-?\-(ver|version)$/) {
+ print_version();
+ exit;
+ } elsif ($args[0]=~/^\-?\-(lic|license)$/) {
+ print_license();
+ exit;
+ }
+ return 1;
+}
+
+# Parses arguments, sets options (global) and returns file list
+sub Parse_Arguments {
+ my @args=@_;
+ Check_Arguments(@args);
+ my @files;
+ foreach my $arg (@ARGV) {
+ if (Parse_Option($arg)) {next;}
+ if ($arg=~/^\-/) {
+ print 'Invalid opton '.$arg."\n";
+ print_syntax();
+ exit;
+ }
+ $arg=~s/\\/\//g;
+ push @files,$arg;
+ }
+ return @files;
+}
+
+# Parse individual option parameters
+sub Parse_Option {
+ my $arg=shift @_;
+ return parse_options_parsing($arg)
+ || parse_options_sums($arg)
+ || parse_options_output($arg)
+ || parse_options_format($arg)
+ ;
+}
+
+sub parse_options_parsing {
+ my $arg=shift @_;
+ if ($arg eq '-inc') {$includeTeX=1;}
+ elsif ($arg eq '-noinc') {$includeTeX=0;}
+ elsif ($arg eq '-dir') {$globalworkdir=undef;}
+ elsif ($arg=~/^-dir=(.*)$/) {$globalworkdir=$1;}
+ elsif ($arg=~/^-(utf8|unicode)$/) {$utf8flag=1;}
+ elsif ($arg=~/^-(ch|chinese|zhongwen)$/) {
+ $utf8flag=1;
+ @WordPatterns=($NamedWordPattern{'chinese'},@WordPatterns);
+ }
+ elsif ($arg=~/^-(jp|japanese)$/) {
+ $utf8flag=1;
+ @WordPatterns=($NamedWordPattern{'japanese'},@WordPatterns);
+ }
+ elsif ($arg eq '-relaxed') {
+ @MacroOptionPatterns=@MacroOptionPatternsRelaxed;
+ $LetterPattern=$LetterPatternRelaxed;
+ }
+ else {return 0;}
+ return 1;
+}
+
+sub parse_options_sums {
+ my $arg=shift @_;
+ if ($arg=~/^-sum(=(.+))?$/) {option_sum($2);}
+ elsif ($arg=~/^-(sub|subcounts?)(=(.+))?$/) {option_subcount($3);}
+ else {return 0;}
+ return 1;
+}
+
+sub option_subcount {
+ my $arg=shift @_;
+ $showsubcounts=1;
+ if (!defined $arg) {
+ %BreakPoints=%{$BreakPointsOptions{'default'}};
+ } elsif (my $option=$BreakPointsOptions{$arg}) {
+ %BreakPoints=%{$option};
+ } else {
+ print STDERR "Warning: Option value ".$arg." not valid, using default instead.\n";
+ %BreakPoints=%{$BreakPointsOptions{'default'}};
+ }
+}
+
+sub option_sum {
+ my $arg=shift @_;
+ if (!defined $arg) {
+ @sumweights=(1,1,1,0,0,1,1);
+ } elsif ($arg=~/^(\d+(,\d+){0,6})$/) {
+ @sumweights=split(',',$1);
+ } else {
+ print STDERR "Warning: Option value ".$arg." not valid, ignoring option.\n";
+ }
+}
+
+sub parse_options_format {
+ my $arg=shift @_;
+ if ($arg eq '-brief') {$briefsum=1;}
+ elsif ($arg eq '-total') {$totalflag=1;}
+ elsif ($arg eq '-1') {$briefsum=1;$totalflag=1;$verbose=-1;}
+ elsif ($arg eq "-html" ) {option_no_colours();$htmlstyle = 2;}
+ elsif ($arg eq "-htmlcore" ) {option_no_colours();$htmlstyle = 1;}
+ elsif ($arg=~/^\-(nocol|nc$)/) {option_no_colours();}
+ elsif ($arg eq '-codes') {
+ $showcodes=2;
+ if ($verbose==0) {$verbose=3;}
+ }
+ elsif ($arg eq '-nocodes') {$showcodes=0;}
+ else {return 0;}
+ return 1;
+}
+
+sub parse_options_output {
+ my $arg=shift @_;
+ if ($arg eq "-v0") {$verbose=0;}
+ elsif ($arg eq "-v1") {$verbose=1;}
+ elsif ($arg eq '-vv' || $arg eq '-v2') {$verbose=2;}
+ elsif ($arg eq '-vvv' || $arg eq '-v3' || $arg eq '-v') {$verbose=3;}
+ elsif ($arg eq '-vvvv' || $arg eq '-v4') {$verbose=3; $showstates=1;}
+ elsif ($arg =~ /^\-showstates?$/ ) {$showstates=1;}
+ elsif ($arg =~ /^-(q|-?quiet)$/ ) {$verbose=-1;}
+ else {return 0;}
+ return 1;
+}
+
+# Parse file list and return total count
+sub parse_file_list {
+ my @filelist=@_;
+ my $listtotalcount=new_count("TOTAL COUNT");
+ for my $file (<@filelist>) {
+ my $filetotalcount=parse_file($file);
+ add_count($listtotalcount,$filetotalcount);
+ }
+ return $listtotalcount;
+}
+
+# Parse file and included files, and return total count
+sub parse_file {
+ my $file=shift @_;
+ $workdir=$globalworkdir;
+ if (!defined $workdir) {
+ $workdir=$file;
+ $workdir =~ s/^((.*[\\\/])?)[^\\\/]+$/$1/;
+ }
+ @filelist=($file);
+ if ($htmlstyle) {print "\n<div class='filegroup'>\n";}
+ my $filetotalcount=new_count("SUM COUNT FOR ".$file);
+ foreach my $f (@filelist) {
+ my $tex=TeXfile($f);
+ my $fpath=$f;
+ $fpath=~s/^((.*[\\\/])?)[^\\\/]+$/$1/;
+ if (!defined $tex) {
+ print STDERR "File not found or not readable: ".$f."\n";
+ formatprint("File not found or not readable: ".$f."\n","p","error");
+ } else {
+ parse($tex);
+ my $filecount=add_subcount($tex);
+ if (!$totalflag) {
+ print_count($filecount);
+ print "\n";
+ }
+ add_count($filetotalcount,$filecount);
+ }
+ }
+ if ($htmlstyle) {print "</div>\n\n";}
+ return $filetotalcount;
+}
+
+
+######
+###### Subroutines
+######
+
+###### CMD specific implementations
+
+
+sub add_file_to_list {
+ my $fname=shift @_;
+ push @filelist,$workdir.$fname;
+}
+
+sub print_with_style {
+ my ($text,$style,$colour)=@_;
+ #if ($utf8flag || $htmlstyle) {utf8::encode($text);}
+ if ($htmlstyle) {
+ print "<span class='".$style."'>".$text."</span>";
+ } else {
+ print Term::ANSIColor::colored($text,$colour);
+ }
+}
+
+sub option_no_colours {
+ $ENV{'ANSI_COLORS_DISABLED'} = 1;
+}
+
+# Print count (total) if conditions are met
+sub conditional_print_total {
+ my $sumcount=shift @_;
+ if ($totalflag || get_count($sumcount,0)>1) {
+ if ($totalflag && $briefsum && @sumweights) {
+ print total_count($sumcount);
+ } else {
+ if ($htmlstyle) {
+ formatprint("Total word count",'h2');
+ }
+ print_count($sumcount);
+ }
+ }
+}
+
+###### Option handling
+
+
+# Apply options to set values
+sub Apply_Options {
+ %STYLE=%{$STYLES[$verbose]};
+ if ($utf8flag) {binmode STDOUT,':utf8';}
+ if ($htmlstyle>1) {html_head();}
+ foreach (@WordPatterns) {
+ s/\@/$LetterPattern/g;
+ }
+}
+
+
+###### TeX code handle
+
+
+sub TeXfile {
+ my $filename=shift @_;
+ my $file=read_file($filename) || return undef;
+ return TeXcode($file,$filename);
+}
+
+sub read_file {
+ my $filename=shift @_;
+ if ($utf8flag) {
+ open(FH,"<:utf8",$filename) || return undef;
+ } else {
+ open(FH,"<".$filename) || return undef;
+ }
+ if ($verbose>0) {
+ formatprint("File: ".$filename."\n",'h2');
+ $blankline=0;
+ }
+ my @text=<FH>;
+ close(FH);
+ my $latexcode=join('',@text);
+ if ($utf8flag) {
+ $latexcode =~ s/^\x{feff}//;
+ }
+ return $latexcode;
+}
+
+###### Parsing routines
+
+
+# Make TeXcode handle
+sub TeXcode {
+ my ($texcode,$filename,$title)=@_;
+ my %TeX=();
+ $TeX{'filename'}=$filename;
+ if (!defined $filename) {
+ $TeX{'filepath'}='';
+ } elsif ($filename=~/^(.*[\\\/])[^\\\/]+$/) {
+ $TeX{'filepath'}=$1;
+ } else {
+ $TeX{'filepath'}='';
+ }
+ if (defined $title) {}
+ elsif (defined $filename) {$title="FILE: ".$filename;}
+ else {$title="Word count";}
+ $TeX{'line'}=$texcode;
+ $TeX{'next'}=undef;
+ $TeX{'type'}=undef;
+ $TeX{'style'}=undef;
+ $TeX{'printstate'}=undef;
+ $TeX{'eof'}=0;
+ my $countsum=new_count($title);
+ $TeX{'countsum'}=$countsum;
+ my $count=new_count("_top_");
+ $TeX{'count'}=$count;
+ inc_count(\%TeX,0);
+ my @countlist=();
+ $TeX{'countlist'}=\@countlist;
+ $countsum->{'subcounts'}=\@countlist;
+ return \%TeX;
+}
+
+# Parse LaTeX document
+sub parse {
+ my ($tex)=@_;
+ if ($htmlstyle && $verbose) {print "<p class=parse>\n";}
+ while (!($tex->{'eof'})) {
+ parse_unit($tex,1);
+ }
+ if ($htmlstyle && $verbose) {print "</p>\n";}
+}
+
+# Parse one block or unit
+sub parse_unit {
+ # Status:
+ # 0 = exclude from count
+ # 1 = text
+ # 2 = header text
+ # 3 = float text
+ # -1 = float (exclude)
+ # -2 = strong exclude, ignore begin-end groups
+ # -3 = stronger exclude, do not parse macro parameters
+ # -9 = preamble (between \documentclass and \begin{document})
+ my ($tex,$status,$end)=@_;
+ if (!defined $status) {
+ print_error("CRITICAL ERROR: Undefined parser status!");
+ exit;
+ } elsif (ref($status) eq 'ARRAY') {
+ print_error("CRITICAL ERROR: Invalid parser status!");
+ exit;
+ }
+ my $substat;
+ if ($showstates) {
+ if (defined $end) {
+ $tex->{'printstate'}=':'.$status.':'.$end.':';
+ } else {
+ $tex->{'printstate'}=':'.$status.':';
+ }
+ flush_next($tex);
+ }
+ while (defined (my $next=next_token($tex))) {
+ # parse next token; or tokens until match with $end
+ set_style($tex,"ignore");
+ if ((defined $end) && ($end eq $next)) {
+ # end of unit
+ return;
+ } elsif (!defined $next) {
+ print_error("ERROR: End of file while waiting for ".$end);
+ return;
+ }
+ if ($status==-9 && $next eq '\begin' && $tex->{'line'}=~/^\{\s*document\s*\}/) {
+ # \begin{document}
+ $status=1;
+ }
+ if ($next eq '\documentclass') {
+ # starts preamble
+ set_style($tex,'document');
+ gobble_option($tex);
+ gobble_macro_parms($tex,1);
+ while (!($tex->{'eof'})) {
+ parse_unit($tex,-9);
+ }
+ } elsif ($tex->{'type'}==666) {
+ # parse TC instructions
+ parse_tc($tex);
+ } elsif ($tex->{'type'}==1) {
+ # word
+ if ($status>0) {
+ inc_count($tex,$status);
+ set_style($tex,'word'.$status);
+ }
+ } elsif ($next eq '{') {
+ # {...}
+ parse_unit($tex,$status,'}');
+ } elsif ($tex->{'type'}==3 && $status==-3) {
+ set_style($tex,'ignore');
+ } elsif ($tex->{'type'}==3) {
+ # macro call
+ parse_macro($tex,$next,$status,$substat);
+ } elsif ($next eq '$') {
+ # math inline
+ parse_math($tex,$status,6,'$');
+ } elsif ($next eq '$$') {
+ # math display (unless already in inlined math)
+ if (!(defined $end && $end eq '$')) {
+ parse_math($tex,$status,7,'$$');
+ }
+ }
+ if (!defined $end) {return;}
+ }
+}
+
+sub parse_macro {
+ my ($tex,$next,$status,$substat)=@_;
+ if (my $label=$BreakPoints{$next}) {
+ if ($tex->{'line'}=~ /^[*]?(\s*\[.*?\])*\s*\{(.+?)\}/ ) {
+ $label=$label.': '.$2;
+ }
+ add_subcount($tex,$label);
+ }
+ set_style($tex,$status>0?'command':'exclcommand');
+ if ($next eq '\begin' && $status!=-2) {
+ parse_begin_end($tex,$status);
+ } elsif (($status==-1) && ($substat=$TeXfloatinc{$next})) {
+ # text included from float
+ set_style($tex,'command');
+ gobble_macro_parms($tex,$substat);
+ } elsif ($status==-9 && defined ($substat=$TeXpreamble{$next})) {
+ # parse preamble include macros
+ set_style($tex,'command');
+ if (defined $TeXheader{$next}) {inc_count($tex,4);}
+ gobble_macro_parms($tex,$substat,1);
+ } elsif ($status<0) {
+ # ignore
+ gobble_option($tex);
+ } elsif ($next eq '\(') {
+ # math inline
+ parse_math($tex,$status,6,'\)');
+ } elsif ($next eq '\[') {
+ # math display
+ parse_math($tex,$status,7,'\]');
+ } elsif ($next eq '\def') {
+ # ignore \def...
+ $tex->{'line'} =~ s/^([^\{]*)\{/\{/;
+ flush_next($tex);
+ print_style($1.' ','ignore');
+ parse_unit($tex,-2);
+ } elsif (defined (my $addsuffix=$TeXfileinclude{$next})) {
+ # include file: queue up for parsing
+ parse_include_file($tex,$status,$addsuffix);
+ } elsif (defined ($substat=$TeXmacro{$next})) {
+ # macro: exclude options
+ if (defined $TeXheader{$next}) {inc_count($tex,4);}
+ gobble_macro_parms($tex,$substat,$status);
+ } elsif (defined ($substat=$TeXmacroword{$next})) {
+ # count macro as word (or a given number of words)
+ inc_count($tex,$status,$substat);
+ set_style($tex,'word'.$status);
+ } elsif ($next =~ /^\\[^\w\_]/) {
+ } else {
+ gobble_option($tex);
+ }
+}
+
+sub parse_tc {
+ my ($tex)=@_;
+ my $next=$tex->{'next'};
+ set_style($tex,'tc');
+ flush_next($tex);
+ if (!($next=~s/^\%+TC:\s*(\w+)\s*// )) {
+ print_error('Warning: TC command should have format %TC:instruction [macro] [parameters]');
+ return;
+ };
+ my $instr=$1;
+ if ($instr=~/^(break)$/) {
+ if ($instr eq 'break') {add_subcount($tex,$next);}
+ } elsif ($next=~/^([\\]*\w+)\s+([^\s\n]+)(\s+([0-9]+))?/) {
+ # Format = TC:word macro
+ my $macro=$1;
+ my $param=$2;
+ my $option=$4;
+ if ($param=~/^\[([0-9,]+)\]$/) {$param=[split(',',$1)];}
+ if (($instr eq 'macro') || ($instr eq 'exclude')) {$TeXmacro{$macro}=$param;}
+ elsif ($instr eq 'header') {$TeXheader{$macro}=$param;$TeXmacro{$macro}=$param;}
+ elsif ($instr eq 'macroword') {$TeXmacroword{$macro}=$param;}
+ elsif ($instr eq 'preambleinclude') {$TeXpreamble{$macro}=$param;}
+ elsif ($instr eq 'group') {
+ $TeXmacro{'begin'.$macro}=$param;
+ $TeXgroup{$macro}=$option;
+ }
+ elsif ($instr eq 'floatinclude') {$TeXfloatinc{$macro}=$param;}
+ elsif ($instr eq 'fileinclude') {$TeXfileinclude{$macro}=$param;}
+ elsif ($instr eq 'breakmacro') {$BreakPoints{$macro}=$param;}
+ else {print_error("Warning: Unknown TC command: ".$instr);}
+ } elsif ($instr eq 'ignore') {
+ tc_ignore_input($tex);
+ } else {
+ print_error("Warning: Invalid TC command format: ".$instr);
+ }
+}
+
+sub tc_ignore_input {
+ my ($tex)=@_;
+ set_style($tex,'ignore');
+ parse_unit($tex,-3,"%TC:endignore");
+ set_style($tex,'tc');
+ flush_next($tex);
+}
+
+sub parse_math {
+ my ($tex,$status,$substat,$end)=@_;
+ my $localstyle=$status>0 ? 'mathgroup' : 'exclmath';
+ if ($status>0) {inc_count($tex,$substat);}
+ set_style($tex,$localstyle);
+ parse_unit($tex,0,$end);
+ set_style($tex,$localstyle);
+}
+
+sub parse_begin_end {
+ my ($tex,$status)=@_;
+ my $localstyle=$status>0 ? 'grouping' : 'exclgroup';
+ flush_style($tex,$localstyle);
+ gobble_option($tex);
+ my $groupname;
+ if ($tex->{'line'} =~ s/^\{\s*([^\{\}]+)\s*\*?\}[ \t\r\f]*//) {
+ # gobble group type
+ $groupname=$1;
+ print_style('{'.$1.'}',$localstyle);
+ my $next='begin'.$1;
+ if (defined (my $substat=$TeXmacro{$next})) {
+ gobble_macro_parms($tex,$substat);
+ }
+ } else {
+ print_error("Warning: BEGIN group without type.");
+ }
+ # find group status (or leave unchanged)
+ my $substat;
+ defined ($substat=$TeXgroup{$1}) || ($substat=$status);
+ if ($status<=0 && $status<$substat) {$substat=$status;}
+ if (($status>0) && ($substat==-1)) {
+ # Count float
+ inc_count($tex,5);
+ }
+ if ($status>0 and $substat>3) {
+ # count item, exclude contents
+ inc_count($tex,$substat);
+ $substat=0;
+ }
+ parse_unit($tex,$substat,'\end');
+ if ($tex->{'line'} =~ s/^\{\s*([^\{\}]+)\s*\}[ \t\r\f]*//) {
+ # gobble group type
+ flush_style($tex,$localstyle);
+ print_style('{'.$1.'}',$localstyle);
+ } else {
+ print_error("Warning: END group without type while waiting to end ".$groupname.".");
+ }
+}
+
+sub parse_include_file {
+ my ($tex,$status,$addsuffix)=@_;
+ $tex->{'line'} =~ s/^\{([^\{\}\s]+)\}// ||
+ $tex->{'line'} =~ s/^\s*([^\{\}\%\\\s]+)// ||
+ return;
+ flush_next($tex);
+ if ($status>0) {
+ print_style($&,'fileinclude');
+ my $fname=$1;
+ if ($addsuffix==2) {$fname.='.tex';}
+ elsif ($addsuffix==1 && ($fname=~/^[^\.]+$/)) {$fname.='.tex';}
+ if ($includeTeX) {add_file_to_list($fname);}
+ } else {
+ print_style($&,'ignored');
+ }
+}
+
+sub gobble_option {
+ my $tex=shift @_;
+ flush_next($tex);
+ foreach my $pattern (@MacroOptionPatterns) {
+ if ($tex->{'line'}=~s/^($pattern)//) {
+ print_style($1,'option');
+ return $1;
+ }
+ }
+ return undef;
+}
+
+sub gobble_options {
+ while (gobble_option(@_)) {}
+}
+
+sub gobble_macro_modifier {
+ my $tex=shift @_;
+ flush_next($tex);
+ if ($tex->{'line'} =~ s/^\*//) {
+ print_style($1,'option');
+ return $1;
+ }
+ return undef;
+}
+
+sub gobble_macro_parms {
+ my ($tex,$parm,$oldstat)=@_;
+ my $i;
+ if (ref($parm) eq 'ARRAY') {
+ $i=scalar @{$parm};
+ } else {
+ $i=$parm;
+ $parm=[0,0,0,0,0,0,0,0,0];
+ }
+ if ($i>0) {gobble_macro_modifier($tex);}
+ gobble_options($tex);
+ for (my $j=0;$j<$i;$j++) {
+ parse_unit($tex,new_status($parm->[$j],$oldstat));
+ gobble_options($tex);
+ }
+}
+
+sub new_status {
+ my ($substat,$old)=@_;
+ if (!defined $old) {return $substat;}
+ if ($old==-3 || $substat==-3) {return -3;}
+ if ($old==-2 || $substat==-2) {return -2;}
+ if ($old==0 || $substat==0) {return 0;}
+ if ($old==-9 || $substat==-9) {return -9;}
+ if ($old>$substat) {return $old;}
+ return $substat;
+}
+
+sub next_token {
+ my $tex=shift @_;
+ my ($next,$type);
+ if (defined $tex->{'next'}) {print_style($tex->{'next'}.' ',$tex->{'style'});}
+ $tex->{'style'}=undef;
+ while (defined ($next=get_next_token($tex))) {
+ $type=$tex->{'type'};
+ if ($type==0) {
+ print_style($next,'comment');
+ } elsif ($type==9) {
+ if ($verbose>0) {line_return(1,$tex);}
+ } else {
+ return $next;
+ }
+ }
+ return $next;
+}
+
+
+sub get_next_token {
+ # Token (or token group) category:
+ # 0: comment
+ # 1: word (or other forms of text or text components)
+ # 2: symbol (not word, e.g. punctuation)
+ # 3: macro
+ # 4: curly braces {}
+ # 5: brackets []
+ # 6: maths
+ # 9: line break in file
+ # 999: end of line or blank line
+ # 666: TeXcount instruction (%TC:instruction)
+ my $tex=shift @_;
+ my $next;
+ (defined ($next=get_token($tex,'\%+TC:\s*endignore\b[^\r\n]*',666))) && return "%TC:endignore";
+ (defined ($next=get_token($tex,'\%+TC:[^\r\n]*',666))) && return $next;
+ (defined ($next=get_token($tex,'\%[^\r\n]*',0))) && return $next;
+ (defined ($next=get_token($tex,'(\r|\n|\r\n)',9))) && return $next;
+ (defined ($next=get_token($tex,'\\\\[\{\}]',2))) && return $next;
+ foreach my $pattern (@WordPatterns) {
+ (defined ($next=get_token($tex,$pattern,1))) && return $next;
+ }
+ (defined ($next=get_token($tex,'[\"\'\`:\.,\(\)\[\]!\+\-\*=/\^\_\@\<\>\~\#\&]',2))) && return $next;
+ (defined ($next=get_token($tex,'\\\\([a-zA-Z_]+|[^a-zA-Z_])',3))) && return $next;
+ (defined ($next=get_token($tex,'[\{\}]',4))) && return $next;
+ (defined ($next=get_token($tex,'[\[\]]',5))) && return $next;
+ (defined ($next=get_token($tex,'\$\$',6))) && return $next;
+ (defined ($next=get_token($tex,'\$',6))) && return $next;
+ (defined ($next=get_token($tex,'.',999))) && return $next;
+ (defined ($next=get_token($tex,'[^\s]+',999))) && return $next;
+ $tex->{'eof'}=1;
+ return undef;
+}
+
+sub get_token {
+ my ($tex,$regexp,$type)=@_;
+ if (!defined $regexp) {print_error("ERROR in get_token: undefined regex.");}
+ if (!defined $tex->{'line'}) {print_error("ERROR in get_token: undefined tex-line. ".$tex->{'next'});}
+ if ( $tex->{'line'} =~ s/^($regexp)[ \t\r\f]*// ) {
+ $tex->{'next'}=$1;
+ $tex->{'type'}=$type;
+ return $1;
+ }
+ return undef;
+}
+
+###### Count handling routines
+
+
+sub new_count {
+ my ($title)=@_;
+ my @cnt=(0,0,0,0,0,0,0,0);
+ my %count=('count'=>\@cnt,'title'=>$title);
+ # files, text words, header words, float words,
+ # headers, floats, math-inline, math-display;
+ return \%count;
+}
+
+sub inc_count {
+ my ($tex,$type,$value)=@_;
+ my $count=$tex->{'count'};
+ if (!defined $value) {$value=1;}
+ ${$count->{'count'}}[$type]+=$value;
+}
+
+sub get_count {
+ my ($count,$type)=@_;
+ return ${$count->{'count'}}[$type];
+}
+
+sub total_count {
+ my ($count)=@_;
+ my $sum=0;
+ for (my $i=scalar(@sumweights);$i-->0;) {
+ $sum+=get_count($count,$i+1)*$sumweights[$i];
+ }
+ return $sum;
+}
+
+sub print_count {
+ my ($count,$header)=@_;
+ if ($briefsum && @sumweights) {
+ print_count_total($count,$header);
+ } elsif ($briefsum) {
+ if ($htmlstyle) {print "<p class='briefcount'>";}
+ print_count_brief($count,$header);
+ if ($htmlstyle) {print "</p>\n";}
+ } else {
+ print_count_details($count,$header);
+ }
+}
+
+sub print_count_with_header {
+ my ($count,$header)=@_;
+ if (!defined $header) {$header=$count->{'title'};}
+ if (!defined $header) {$header="";}
+ return $count,$header;
+}
+
+sub print_count_total {
+ my ($count,$header)=print_count_with_header(@_);
+ if ($htmlstyle) {print "<p class='count'>".$header;}
+ print total_count($count);
+ if ($htmlstyle) {print "</p>\n";}
+ else {print ": ".$header;}
+}
+
+sub print_count_brief {
+ my ($count,$header)=print_count_with_header(@_);
+ my $cnt=$count->{'count'};
+ print ${$cnt}[1]."+".${$cnt}[2]."+".${$cnt}[3].
+ " (".${$cnt}[4]."/".${$cnt}[5]."/".${$cnt}[6]."/".${$cnt}[7].") ".
+ $header;
+}
+
+sub print_count_details {
+ my ($count,$header)=print_count_with_header(@_);
+ if ($htmlstyle) {print "<dl class='count'>\n";}
+ if (defined $header) {
+ formatprint($header."\n",'dt','header');
+ }
+ if (get_count($count,0)>1) {
+ formatprint($countlabel[0].': ','dt');
+ formatprint(get_count($count,0)."\n",'dd');
+ }
+ if (@sumweights) {
+ formatprint('Sum count: ','dt');
+ formatprint(total_count($count)."\n",'dd');
+ }
+ for (my $i=1;$i<8;$i++) {
+ formatprint($countlabel[$i].': ','dt');
+ formatprint(get_count($count,$i)."\n",'dd');
+ }
+ my $subcounts=$count->{'subcounts'};
+ if ($showsubcounts && defined $subcounts && scalar(@{$subcounts})>1) {
+ formatprint("Subcounts: text+headers+captions (#headers/#floats/#inlines/#displayed)\n",'dt');
+ foreach my $subcount (@{$subcounts}) {
+ if ($htmlstyle) {print "<dd class='briefcount'>";}
+ print_count_brief($subcount);
+ if ($htmlstyle) {print "</dd>";}
+ print "\n";
+ }
+ }
+ if ($htmlstyle) {print "</dl>\n";}
+}
+
+sub add_count {
+ my ($a,$b)=@_;
+ for (my $i=0;$i<8;$i++) {
+ ${$a->{'count'}}[$i]+=${$b->{'count'}}[$i];
+ }
+}
+
+sub add_subcount {
+ my ($tex,$title)=@_;
+ add_count($tex->{'countsum'},$tex->{'count'});
+ push @{$tex->{'countlist'}},$tex->{'count'};
+ $tex->{'count'}=new_count($title);
+ return $tex->{'countsum'};
+}
+
+###### Printing routines
+
+
+sub set_style {
+ my ($tex,$style)=@_;
+ if (!(($tex->{'style'}) && ($tex->{'style'} eq '-'))) {$tex->{'style'}=$style;}
+}
+
+sub flush_style {
+ my ($tex,$style)=@_;
+ set_style($tex,$style);
+ flush_next($tex);
+}
+
+sub line_return {
+ my ($blank,$tex)=@_;
+ if ($blank>$blankline) {
+ if ((defined $tex) && @sumweights) {
+ my $num=total_count($tex->{'count'});
+ print_style(" [".$num."]","sumcount");
+ }
+ linebreak();
+ $blankline++;
+ }
+}
+
+sub linebreak {
+ if ($htmlstyle) {print "<br>\n";} else {print "\n";}
+}
+
+sub print_style {
+ my ($text,$style,$state)=@_;
+ (($verbose>=0) && (defined $text) && (defined $style)) || return 0;
+ my $colour;
+ ($colour=$STYLE{$style}) || return;
+ if (($colour) && !($colour eq '-')) {
+ print_with_style($text,$style,$colour);
+ if ($state) {
+ print_style($state,'state');
+ }
+ $blankline=-1;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+sub print_error {
+ my $text=shift @_;
+ $errorcount++;
+ if ($verbose>=0) {
+ line_return(1);
+ print_style("!!! ".$text." !!!",'error');
+ line_return(1);
+ }
+}
+
+sub formatprint {
+ my ($text,$tag,$class)=@_;
+ my $break=($text=~s/\n$//);
+ if ($htmlstyle && defined $tag) {
+ print '<'.$tag;
+ if ($class) {print " class='".$class."'";}
+ print '>'.$text.'</'.$tag.'>';
+ } else {
+ print $text;
+ }
+ if ($break) {print "\n";}
+}
+
+sub flush_next {
+ my $tex=shift @_;
+ if (defined $tex->{'next'}) {
+ print_style($tex->{'next'}.' ',$tex->{'style'},$tex->{'printstate'});
+ }
+ $tex->{'printstate'}=undef;
+ $tex->{'style'}='-';
+}
+
+
+# Close the output, e.g. adding HTML tail
+sub Close_Output {
+ if ($htmlstyle>1) {
+ html_tail();
+ }
+}
+
+
+# Report if there were any errors occurring during parsing
+sub Report_ErrorCount {
+ if ($errorcount==0) {return;}
+ if ($briefsum && $totalflag) {print " ";}
+ if ($htmlstyle) {
+ print_error("Errors:".$errorcount,"p","error");
+ } else {
+ print "(errors:".$errorcount.")";
+ }
+}
+
+
+sub print_help_style {
+ if ($verbose<=0) {return;}
+ formatprint("Format/colour codes of verbose output:","h2");
+ print "\n\n";
+ if ($htmlstyle) {print "<p class='stylehelp'>";}
+ help_style_line('Text which is counted',"word1","counted as text words");
+ help_style_line('Header and title text',"word2","counted as header words");
+ help_style_line('Caption text and footnotes',"word3","counted as caption words");
+ help_style_line("Ignored text or code","ignore","excluded or ignored");
+ help_style_line('\documentclass',"document","document start, beginning of preamble");
+ help_style_line('\macro',"command","macro not counted, but parameters may be");
+ help_style_line('\macro',"exclcommand","macro in excluded region");
+ help_style_line("[Macro options]","option","not counted");
+ help_style_line('\begin{group} \end{group}',"grouping","begin/end group");
+ help_style_line('\begin{group} \end{group}',"exclgroup","begin/end group in excluded region");
+ help_style_line('$ $',"mathgroup","counted as one equation");
+ help_style_line('$ $',"exclmath","equation in excluded region");
+ help_style_line('% Comments',"comment","not counted");
+ help_style_line('%TC:TeXcount instructions',"tc","not counted");
+ help_style_line("File to include","fileinclude","not counted but file may be counted later");
+ if ($showstates) {
+ help_style_line('[state]',"state","internal TeXcount state");
+ }
+ if (@sumweights) {
+ help_style_line('[sumcount]',"sumcount","cumulative sum count");
+ }
+ help_style_line("ERROR","error","TeXcount error message");
+ if ($htmlstyle) {print "</p>";}
+ print "\n\n";
+}
+
+sub help_style_line {
+ my ($text,$style,$comment)=@_;
+ if ($htmlstyle) {
+ $comment="&nbsp;&nbsp;....&nbsp;&nbsp;".$comment;
+ } else {
+ $comment=" .... ".$comment;
+ }
+ if (print_style($text,$style)) {
+ print $comment;
+ linebreak();
+ }
+}
+
+# Print output style codes if conditions are met
+sub conditional_print_help_style {
+ if ($showcodes) {print_help_style();}
+ return $showcodes;
+}
+
+###### HTML routines
+
+
+
+sub html_head {
+ print "<html>\n<head>";
+ if ($utf8flag) {
+ print "\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">";
+ }
+ print '
+<style>
+<!--
+body {width:auto;padding:5;margin:5;}
+.error {font-weight:bold;color:#f00;font-style:italic;}
+.word1,.word2,.word3 {color: #009;}
+.word2 {font-weight: 700;}
+.word3 {font-style: italic;}
+.command {color: #c00;}
+.exclcommand {color: #f99;}
+.option {color: #cc0;}
+.grouping, .document {color: #900; font-weight:bold;}
+.mathgroup {color: #090;}
+.exclmath {color: #6c6;}
+.ignore {color: #999;}
+.exclgroup {color:#c66;}
+.tc {color: #999; font-weight:bold;}
+.comment {color: #999; font-style: italic;}
+.state {color: #990; font-size: 70%;}
+.sumcount {color: #999; font-size: 80%;}
+.fileinclude {color: #696; font-weight:bold;}
+dl.count {background: #cfc; color: 009;}
+dl.count dt.header {font-weight: bold; font-style: italic; float: none;}
+dl.count dt {clear: both; float: left; margin-right: .5em;}
+dl.count dd {font-weight: bold;}
+dl.count dd.briefcount {font-weight: 700; clear: both; font-size:80%; font-weight:normal; margin-left:8pt;}
+.warning {color: #c00; font-weight: 700;}
+.parse, .count, .stylehelp, .filegroup {border: solid 1px #999; margin: 0pt; padding: 4pt;}
+.parse {font-size: 80%; background: #eef;}
+.parse {border-bottom:none;}
+.stylehelp {font-size: 80%; background: #ffc; margin-bottom: 8pt;}
+.filegroup {background: #efe; margin-bottom: 8pt;}
+-->
+</style>
+</head>
+<body>
+<h1>LaTeX word count</h1>
+';
+}
+
+sub html_tail {
+ print '</body></html>';
+}
+
+###### Help routines
+
+
+
+sub print_version {
+ print "TeXcount version ".$versionnumber.", ".$versiondate.'.';
+}
+
+sub print_syntax {
+ print '
+Syntax: TeXcount.pl [options] files
+
+Options:
+ -relaxed Uses relaxed rules for word and option handling:
+ i.e. allows more general cases to be counted as
+ either words or macros.
+ -v Verbose (same as -v3)
+ -v0 Do not present parsing details
+ -v1 Verbose: print parsed words, mark formulae
+ -v2 More verbose: also print ignored text
+ -v3 Even more verbose: include comments and options
+ -v4 Same as -v3 -showstate
+ -showstate Show internal states (with verbose)
+ -brief Only prints a brief, one line summary of counts
+ -q, -quiet Quiet mode, no error messages (use is discouraged!)
+ -sum, -sum= Make sum of all word and equation counts. May also
+ use -sum=#[,#] with up to 7 numbers to indicate how
+ each of the counts (text words, header words, caption
+ words, #headers, #floats, #inlined formulae,
+ #displayed formulae) are summed. The default sum (if
+ only -sum is used) is the same as -sum=1,1,1,0,0,1,1.
+ -sub, -sub= Generate subcounts. Option values are none, part,
+ chapter, section or subsection. Default (-sub) is set
+ to subsection, whereas unset is none. (Alternative
+ option name is -subcount.)
+ -nc, -nocol No colours (colours require ANSI)
+ -html Output in HTML format
+ -htmlcore Only HTML body contents
+ -inc Include tex files included in the document
+ -noinc Do not include included tex files (default)
+ -total Do not give sums per file, only total sum.
+ -1 Same as -brief and -total. Ensures there is only one
+ line of output. If used in conjunction with -sum, the
+ output will only be the total number. (NB: Character
+ is the number one, not the letter L.)
+ -dir, -dir= Specify the working directory using -dir=path.
+ Remember that the path must end with \ or /. If only
+ -dir is used, the directory of the parent file is used.
+ -utf8, -unicode Turns on Unicode (UTF-8) for input and output. This
+ is automatic with -chinese, and is required to handle
+ e.g. Korean text. Note that the TeX file must be save
+ in UTF-8 format (not e.g. GB2312 or Big5), or the
+ result will be unpredictable.
+ -ch, -chinese, -zhongwen Turns on support for Chinese characters.
+ TeXcount will then count each Chinese character as a
+ word. Automatically turns on -utf8.
+ -jp, -japanese Turns on support for Japanese characters. TeXcount
+ will count each Japanese character (kanji, hiragana,
+ and katakana) as one word, i.e. not do any form of
+ word segmentation. Automatically turns on -utf8.
+ -codes Display output style code overview and explanation.
+ This is on by default.
+ -nocodes Do not display output style code overview.
+ -h, -?, --help, /? Help
+ --version Print version number
+ --license License information
+';
+}
+
+sub print_help {
+ print '
+***************************************************************
+* TeXcount.pl '.$versionnumber.', '.$versiondate.'
+*
+
+Count words in TeX and LaTeX files, ignoring macros, tables,
+formulae, etc.
+';
+ print_syntax();
+ print '
+The script counts words as either words in the text, words in
+headers/titles or words in floats (figure/table captions).
+Macro options (i.e. \marco[...]) are ignored; macro parameters
+(i.e. \macro{...}) are counted or ignored depending on the
+macro, but by default counted. Begin-end groups are by default
+ignored and treated as \'floats\', though some (e.g. center) are
+counted.
+
+Unless -nocol (or -nc) has been specified, the output will be
+colour coded. Counted text is coloured blue with headers are in
+bold and in HTML output caption text is italicised.
+
+Mathematical formulae are not counted as words, but are instead
+counted separately with separate counts for inlined formulae
+and displayed formulae. Similarly, the number of headers and
+the number of \'floats\' are counted. Note that \'float\' is used
+here to describe anything defined in a begin-end group unless
+explicitly recognized as text or mathematics.
+
+The verbose options (-v1, -v2, -v3, showstate) produces output
+indicating how the text has been interpreted. Check this to
+ensure that words in the text has been interpreted as such,
+whereas mathematical formulae and text/non-text in begin-end
+groups have been correctly interpreted.
+
+Parsing instructions may be passed to TeXcount using comments
+in the LaTeX files on the format
+ %TC:instruction arguments
+where valid instructions for setting parsing rules, typically
+set at the start of the document (applies globally), are:
+ %TC:macro [macro] [param.states]
+ macro handling rule, no. of and rules for parameters
+ %TC:macroword [macro] [number]
+ macro counted as a given number of words
+ %TC:header [macro] [param.states]
+ header macro rule, as macro but counts as one header
+ %TC:breakmacro [macro] [label]
+ macro causing subcount break point
+ %TC:group [name] [parsing-state]
+ begin-end-group handling rule
+ %TC:floatinclude [macro] [param.states]
+ as macro, but also counted inside floats
+ %TC:preambleinclude [macro] [param.states]
+ as macro, but also counted inside the preamble
+ %TC:fileinclue [macro] [rule]
+ file include, add .tex if rule=2, not if rule=0
+The [param.states] is used to indicate the number of parameters
+used by the macro and the rules of handling each of these: format
+is [#,#,...,#] with one number for each parameter, and main rules
+are 0 to ignore and 1 to count as text. Parsing instructions
+which may be used anywhere are:
+ %TC:ignore start block to ignore
+ %TC:endignore end block to ignore
+ %TC:break [title] add subcount break point here
+See the documentation for more details.
+
+Unix hint: Use \'less -r\' instead of just \'less\' to view output:
+the \'-r\' option makes less treat text formating codes properly.
+
+Windows hint: If your Windows interprets ANSI colour codes, lucky
+you! Otherwise, use the -nocol (or -nc) option with the verbose
+options or the output will be riddled with colour codes. Instead,
+you can use -html to produce HTML code, write this to file and
+view with your favourite browser.
+';
+ print_reference();
+}
+
+sub print_reference {
+ print '
+The TeXcount script is copyright of Einar Andreas Rødland (2008)
+and published under the LaTeX Project Public License.
+
+For more information about the script, e.g. news, updates, help,
+usage tips, known issues and short-comings, go to
+ http://folk.uio.no/einarro/Comp/texwordcount.html
+or go to
+ http://folk.uio.no/einarro/Services/texcount.html
+to access the script as a web service. Feedback such as problems
+or errors can be reported to einarro@ifi.uio.no.
+';
+}
+
+sub print_license {
+ print 'TeXcount version '.$versionnumber.'
+
+Copyright 2008 Einar Andreas Rødland
+
+The TeXcount script is published under the LaTeX Project Public
+License (LPPL)
+ http://www.latex-project.org/lppl.txt
+which grants you, the user, the right to use, modify and distribute
+the script. However, if the script is modified, you must change its
+name or use other technical means to avoid confusion.
+
+The script has LPPL status "maintained" with Einar Andreas
+Rødland being the current maintainer.
+';
+}
+
diff --git a/bin/torrentSort.py b/bin/torrentSort.py
new file mode 100644
index 0000000..e284a88
--- /dev/null
+++ b/bin/torrentSort.py
@@ -0,0 +1,220 @@
+#! /usr/bin/python
+# simple python script to sort torrents
+# modifications by xkonni
+# - added folder sorting
+# - added htdigest authorisation
+# - fixed some minor glitches
+#
+# original version by jonassw from
+# http://forum.xbmc.org/showthread.php?t=60749
+
+# << DOCUMENTATION
+#
+# i) lighttpd configuration for htdigest
+# server.modules += ( "mod_auth" )
+# auth.backend = "htdigest"
+# auth.backend.htdigest.userfile = "/etc/lighttpd/auth"
+# auth.debug = 2
+# auth.require = ( "/RPC2" =>
+# (
+# "method" => "digest",
+# "realm" => "REALM",
+# "require" => "valid-user"
+# )
+# )
+# ii) i recommend starting this with a basic cron job you may find other
+# suggestions like when rtorrent finishes hashing, but for me this caused
+# lockups when starting rtorrent (as all downloads return hash_ok)
+#
+# */5 * * * * for i in /mnt/torrent/tv/*; do torrentSort.py $i > /dev/null; done
+#
+# DOCUMENTATION >>
+
+
+import xmlrpclib, os, sys, re, shutil
+
+
+class HTTPSDigestTransport(xmlrpclib.SafeTransport):
+ """
+Transport that uses urllib2 so that we can do Digest authentication.
+Based upon code at http://bytes.com/topic/python/answers/509382-solution-xml-rpc-over-proxy
+"""
+
+ def __init__(self, username, pw, realm, verbose = None, use_datetime=0):
+ self.__username = username
+ self.__pw = pw
+ self.__realm = realm
+ self.verbose = verbose
+ self._use_datetime = use_datetime
+
+ def request(self, host, handler, request_body, verbose):
+ import urllib2
+
+ url='https://'+host+handler
+ if verbose or self.verbose:
+ print "ProxyTransport URL: [%s]"%url
+
+ request = urllib2.Request(url)
+ request.add_data(request_body)
+ # Note: 'Host' and 'Content-Length' are added automatically
+ request.add_header("User-Agent", self.user_agent)
+ request.add_header("Content-Type", "text/xml") # Important
+
+ # setup digest authentication
+ authhandler = urllib2.HTTPDigestAuthHandler()
+ authhandler.add_password(self.__realm, url, self.__username, self.__pw)
+ opener = urllib2.build_opener(authhandler)
+
+ #proxy_handler=urllib2.ProxyHandler()
+ #opener=urllib2.build_opener(proxy_handler)
+ f=opener.open(request)
+ return(self.parse_response(f))
+
+
+def adoptionCandidates(basedir, filename):
+ dirs = filter(lambda x : os.path.isdir(os.path.join(basedir, x)), os.listdir(basedir))
+
+ #set filename to lowercase for string comparisons
+ filename=filename.lower()
+
+ ignoredPhrases = ['-','_']
+
+ candidates = []
+ for dir in dirs:
+ dirParts = dir.split()
+ score = 0
+ requiredScore = 0
+
+ for part in dirParts:
+ if ignoredPhrases.count(part) > 0:
+ continue
+ requiredScore = requiredScore + 1
+
+ #force lower case for string comparison.
+ part=part.lower()
+ # replace "'" with "" and add word to list
+ repPart = part.replace('\'','')
+ if repPart != part:
+ dirParts.append(repPart)
+ requiredScore -= 1
+ if filename.find(part) >= 0:
+ score = score + 1
+ if score == requiredScore:
+ candidates.append( (os.path.join(basedir, dir), score) )
+
+ return candidates
+
+def getSeasonNumber(filename):
+ patterns = [
+ '.*S(\d+)E(\d+).*',
+ '.*S(\d+)(\.)?E(\d+).*', # hopefully matches series.s01.e05.avi
+ '(\d+)x(\d+).*',
+ '(\d+)(\d+)(\d+).*'
+ # commented out regex thought below Season was '4' not 14. Def better way of doing that
+ #top_gear.14x04.720p_hdtv_x264-fov.mkv
+ #'.*(\d+)x(\d+).*'
+ ]
+
+ for pattern in patterns:
+ p = re.compile(pattern, re.I)
+ g = p.findall(orphanFile)
+ if len(g) > 0:
+ season = int(g[0][0])
+ return season
+ return None
+
+
+def getRtorrentId(filename):
+ downloads = rtorrent.download_list('')
+ for dl in downloads:
+ rfile = rtorrent.d.get_base_filename(dl)
+ if rfile == filename:
+ return dl
+
+# << CONFIGURATION
+# i) FOLDER SETTINGS
+showLocations = ['/mnt/media/tv']
+allowedSourceLocation = '/mnt/media/torrent/complete/tv'
+
+# ii) CONNECTION SETTINGS
+# - using htdigest authorisation
+digestTransport = HTTPSDigestTransport("username", "password", "realm")
+rtorrent = xmlrpclib.ServerProxy('http://localhost',transport=digestTransport)
+# - not using authorisation
+# rtorrent = xmlrpclib.ServerProxy('http://localhost')
+
+# CONFIGURATION >>
+
+print '--------------- BEGIN ---------------'
+orphanFile = sys.argv[1]
+if os.path.isdir(orphanFile):
+ (fpath, fname) = os.path.split(orphanFile)
+ fname = os.path.relpath(orphanFile, allowedSourceLocation)
+
+else:
+ (fpath, fname) = os.path.split(orphanFile)
+print 'File path: %s' % fpath
+print 'File name: %s' % fname
+
+candidates = []
+
+if not orphanFile.startswith(allowedSourceLocation):
+ print 'STOP! This file is not located in %s' % allowedSourceLocation
+ exit()
+
+print 'Attempting to find a home for file %s' % orphanFile
+
+for location in showLocations:
+ candidates.extend(adoptionCandidates(location, fname))
+
+candidates.sort(lambda (da, sa), (db, sb): sb-sa)
+
+if len(candidates) <= 0:
+ print 'No one wanted this file :('
+ exit()
+
+for (dir, score) in candidates:
+ print 'Candidate: %s with score %i' % (dir, score)
+
+print 'Winner is %s with score %i' % candidates[0]
+
+if os.path.isdir(orphanFile):
+ finaldir = candidates[0][0]
+
+else:
+ # Determine Season and Episode number
+ season = getSeasonNumber(fname)
+ if not season:
+ print 'STOP! Season could not be determined.'
+ exit()
+
+ print 'Season was determined to be %i' % season
+ finaldir = os.path.join(candidates[0][0], 'Season %s' % season)
+
+ # Check if season folder is present
+ if not os.path.isdir(finaldir):
+ print 'Season dir doesn\'t exist. Creating now'
+ os.mkdir(finaldir)
+
+ if os.path.isfile(os.path.join(finaldir, fname)):
+ print 'error: file already exists, exiting'
+ sys.exit(1)
+
+ print 'Will move file to %s' % finaldir
+
+
+print 'Requesting id from rtorrent'
+rid = getRtorrentId(fname)
+print '%s was resolved to rtorrent id: %s' % (fname, rid)
+
+print 'Pausing rtorrent'
+rtorrent.d.pause(rid)
+
+print 'Updating rtorrent'
+rtorrent.d.set_directory(rid, finaldir)
+
+print 'Moving file'
+shutil.move(orphanFile, finaldir)
+
+print 'Resuming rtorrent'
+rtorrent.d.resume(rid)