From: Nick Bowler Date: Fri, 7 Dec 2012 00:37:29 +0000 (-0500) Subject: Import the scripts directory from my website. X-Git-Url: http://git.draconx.ca/gitweb/scripts.git/commitdiff_plain/3c3894593e94776825542cd016f2bb2b75036fbd Import the scripts directory from my website. --- 3c3894593e94776825542cd016f2bb2b75036fbd diff --git a/HEADER.txt b/HEADER.txt new file mode 100644 index 0000000..870ffdf --- /dev/null +++ b/HEADER.txt @@ -0,0 +1,4 @@ +Various short programs. Generally, they are described in comments at the top +of the source file. Most of these programs are free to be copied, modified +and/or distributed under the terms of the Do What The Fuck You Want To Public +License, version 2. There is NO WARRANTY, to the extent permitted by law. diff --git a/WTFPL-2 b/WTFPL-2 new file mode 100644 index 0000000..cb33446 --- /dev/null +++ b/WTFPL-2 @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + 22 rue de Plaisance, 75014 Paris, France + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/c-is-not-cxx.c b/c-is-not-cxx.c new file mode 100644 index 0000000..a942b82 --- /dev/null +++ b/c-is-not-cxx.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2008-2010 Nick Bowler + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + * + * This is, to the best of my knowledge, a strictly conforming ANSI C + * (C89) program which is not a C++ program. This proves by counter- + * example that C++ is not a superset of C. + * + * Some of the "techniques" used in this file are stupid legacy features. + * Please don't take this as an example of a well-written C program. + * + * The output of this program should be: + * The old answer was 42, but the new answer is 54. + * + * There are more than 10 different things wrong with this program if you + * consider it as C++. Can you spot them all? This is not valid C99 for + * a few of these reasons. For now, it is pointless to update this to + * C99 -- there are just too many obvious incompatibilities. + */ + +#include +#include + +#define and 42 +#define or 54 + +static struct baz { + struct foo { + enum { + ANSWER1 = and, + ANSWER2 = or, + ANSWER3 = -ANSWER1 + } val; + } foo; +}; + +const int foo; +typedef int baz(); +baz main; + +int main(argc, argv) + int argc; + char **argv; +{ + struct foo foo = { 0 }; + int *new; + + if (argc >= foo.val) { + return main(bar(), argv); + } + + goto assign; + { + int old = 0; + assign: + old = -argc; + + new = malloc(sizeof 'x'); + if (!new) { + perror("malloc"); + return EXIT_FAILURE; + } + + *new = ANSWER2; + printf("The old answer was %d, but the new answer is %d.\n", + old, *new); + free(new); + } + + return 0; +} + +bar() +{ + return (enum{X = ANSWER3})-X//**/-1 + ; +} diff --git a/clean-modules.zsh b/clean-modules.zsh new file mode 100755 index 0000000..4ea8251 --- /dev/null +++ b/clean-modules.zsh @@ -0,0 +1,46 @@ +#!/usr/bin/env zsh +# +# Copyright © 2012 Nick Bowler +# +# Prunes directories in /lib/modules that do not correspond to any kernel +# version in /boot. If the -f option is not specified, only print out what +# will be removed. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +force=no +verbose=no +while getopts 'fv' opt $@; do + case $opt in + f) force=yes ;; + v) verbose=yes ;; + \?) exit 1 ;; + esac +done + +[[ force = yes ]] || verbose=yes + +boot=/boot +kernbase=$boot/vmlinuz- +kernels=($kernbase*(N)) + +if ! [[ $#kernels -ge 1 ]]; then + printf '%s: no kernels found in %s, aborting\n' "$0" "$boot" + exit 1 +fi + +typeset -A kernmap +for v in ${${kernels%.old}#$kernbase}; do + kernmap[$v]=false +done + +modbase=/lib/modules +for d in $modbase/*; do + v=${d#$modbase/} + if $kernmap[$v] :; then + [[ $verbose = yes ]] && printf '%s\n' "$d" + [[ $force = yes ]] && rm -rf "$d" + fi +done diff --git a/cmdwrap.c b/cmdwrap.c new file mode 100644 index 0000000..f146c8b --- /dev/null +++ b/cmdwrap.c @@ -0,0 +1,120 @@ +/* Copyright (C) 2010 Nick Bowler + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + * + * Putting . in PATH is generally not a good idea, for a variety of reasons. + * However, some programs expect it to be there, and do not function correctly + * without it. This program allows one to invoke specific programs in the + * current working directory as though . was in the PATH without it actually + * being there. + * + * To work, place a copy of (or symlink to) this program somewhere that is + * actually in the PATH. The name of the command determines what program + * gets invoked. Basic checks are done to avoid accidental infinite + * recursion. + * + * This is probably not portable to non-GNU/Linux systems. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + +/* Common shell return codes */ +#define SHELL_CMD_NOT_FOUND 127 +#define SHELL_CMD_NOT_EXEC 126 + +/* + * A wrapper around readlink which dynamically allocates a buffer large + * enough to contain the entire filename. Also add a null terminator + * so that the filename can be used as a C string. + */ +char *readlink_alloc(const char *path) +{ + ssize_t bufsize = 64, rc; + char *buf = NULL, *tmp; + + while (1) { + if (bufsize >= SSIZE_MAX/2) { + fprintf(stderr, "cmdwrap: %s: %s\n", + path, strerror(ENAMETOOLONG)); + break; + } + + tmp = realloc(buf, bufsize * 2); + if (!tmp) { + fprintf(stderr, "cmdwrap: %s\n", strerror(errno)); + break; + } + + bufsize *= 2; + buf = tmp; + + rc = readlink(path, buf, bufsize); + if (rc == -1) { + fprintf(stderr, "cmdwrap: %s: %s\n", + path, strerror(errno)); + break; + } else if (rc < bufsize) { + buf[rc] = 0; + return buf; + } + } + + free(buf); + return NULL; +} + +int main(int argc, char **argv) +{ + char *curexe, *newexe, fdname[64]; + int fd; + + if (argc < 1) { + fprintf(stderr, "cmdwrap: command name must be present in the argument list.\n"); + return SHELL_CMD_NOT_FOUND; + } + + fd = open(argv[0], O_RDONLY); + if (fd == -1) { + fprintf(stderr, "cmdwrap: %s: %s\n", argv[0], strerror(errno)); + return SHELL_CMD_NOT_FOUND; + } + + /* Try to avoid accidental infinite recursion. */ + sprintf(fdname, "/proc/self/fd/%d", fd); + newexe = readlink_alloc(fdname); + if (!newexe) + return SHELL_CMD_NOT_FOUND; + + curexe = readlink_alloc("/proc/self/exe"); + if (!curexe) + return SHELL_CMD_NOT_FOUND; + + if (strcmp(newexe, curexe) == 0) { + fprintf(stderr, "cmdwrap: infinite recursion detected\n"); + return SHELL_CMD_NOT_EXEC; + } + + /* + * Some programs expect argv[0] to point to their actual executable + * (potentially after a path lookup). But if we leave argv[0] as it + * is, they'll tend to find this program instead of the right one. + * + * Fix that up, too. + */ + argv[0] = newexe; + free(curexe); + + fexecve(fd, argv, environ); + fprintf(stderr, "cmdwrap: %s: %s\n", argv[0], strerror(errno)); + return SHELL_CMD_NOT_EXEC; +} diff --git a/dpicalc.c b/dpicalc.c new file mode 100644 index 0000000..42b4d18 --- /dev/null +++ b/dpicalc.c @@ -0,0 +1,66 @@ +/* + * Copyright © 2011 Nick Bowler + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + */ + +#include +#include +#include +#include +#include + +const char *progname = "dpicalc"; + +static unsigned long +simple_strtoul(const char *str, const char *name, int base) +{ + unsigned long ret; + char *end; + + errno = 0; + ret = strtoul(str, &end, base); + if (errno != 0) { + fprintf(stderr, "%s: %s: invalid %s: %s\n", + progname, str, name, strerror(errno)); + exit(EXIT_FAILURE); + } else if (*end != '\0') { + fprintf(stderr, "%s: %s: invalid %s: trailing garbage\n", + progname, str, name); + exit(EXIT_FAILURE); + } + + return ret; +} + +int main(int argc, char **argv) +{ + double diagonal, ratio, phys_width; + unsigned long w, h; + + if (argc > 0) + progname = argv[0]; + + if (argc < 4) { + fprintf(stderr, "usage: %s diagonal pixel_w pixel_h\n", + progname); + return EXIT_FAILURE; + } + + diagonal = strtod(argv[1], NULL); + w = simple_strtoul(argv[2], "pixel width", 10); + h = simple_strtoul(argv[3], "pixel height", 10); + + ratio = (double) h / w; + if (!isnormal(ratio)) { + fprintf(stderr, "%s: nonsensical aspect ratio (%.2f)\n", + progname, ratio); + } + + phys_width = sqrt(diagonal*diagonal / (1 + ratio*ratio)); + printf("%.1f\n", w / phys_width); + + return 0; +} diff --git a/fetchmail-sync.zsh b/fetchmail-sync.zsh new file mode 100755 index 0000000..45a4e01 --- /dev/null +++ b/fetchmail-sync.zsh @@ -0,0 +1,28 @@ +#!/usr/bin/env zsh +# +# Copyright (C) 2010 Nick Bowler +# +# Simple script which ensures that a running fetchmail daemon is +# sleeping, by forcing a fetch run and waiting until it finishes. +# This is useful for log rotation: the fetchmail daemon will not +# write to the log file while it is sleeping. It is also useful +# for manually invoking fetch runs. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +setopt nonotify + +pid=`head -n 1 ~/.fetchmail.pid 2>/dev/null` + +if ! [[ $pid > 0 && $(ps --no-heading $pid) =~ fetchmail ]]; then + echo "$0: fetchmail is not running." + exit 1 +fi + +coproc sed --unbuffered '/^fetchmail: sleeping/q' +tail -s 0.1 -n 0 -f ~/.fetchmaillog --pid $! >&p & +kill -USR1 $pid +<&p +wait diff --git a/fucked.scheme b/fucked.scheme new file mode 100644 index 0000000..5f3235b --- /dev/null +++ b/fucked.scheme @@ -0,0 +1,2 @@ +(define fucked (lambda (x) (display "fucked\n") (x x))) +(fucked (call-with-current-continuation (lambda (k) (k k)))) diff --git a/genlibs.sh b/genlibs.sh new file mode 100755 index 0000000..3f28fea --- /dev/null +++ b/genlibs.sh @@ -0,0 +1,28 @@ +#!/bin/zsh +# +# Copyright (C) 2009 Nick Bowler +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. +# +# Generate library dependencies for an initramfs. Finds all dynamically linked +# binaries listed in an initramfs description (as needed by gen_init_cpio from +# the Linux source) and passes them to libs.sh in order to actually generate +# the dependencies. + +if [ -z "$1" ]; then + echo "usage: genlibs.sh initramfs-desc" + exit 1 +fi + +files=(`sed -n 's/^ *file \+[^ ]\+ \+\([^ ]\+\).*/\1/p' "$1"`) + +dyns=() + +for i in $files; do + if ! file "$i" | grep dynamic >/dev/null; then continue; fi + dyns+="$i" +done + +./libs.sh $dyns diff --git a/get_extension.zsh b/get_extension.zsh new file mode 100755 index 0000000..090af8e --- /dev/null +++ b/get_extension.zsh @@ -0,0 +1,46 @@ +#!/usr/bin/env zsh +# +# Copyright © 2010-2012 Nick Bowler +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. +# +# A convenient script to download an OpenGL extension specification from the +# Khronos registry into the current working directory. + +IFS=_ extension=(${=1}) + +case $extension[1] in +GL|GLX|WGL) + ;; +*) + extension=(GL $extension) + ;; +esac + +api=$extension[1] +vendor=$extension[2] +name=${(j:_:)extension[3,-1]} +fullname=${(j:_:)extension} + +if [[ -z $api || -z $vendor || -z $name ]]; then + printf 'usage: %s extension\n' $0 1>&2 + exit 1 +fi + +urls=( + http://www.opengl.org/registry/specs/$vendor/$name.txt + http://www.opengl.org/registry/specs/$vendor/${api:l}_$name.txt +) + +for url in $urls; do + if wget -O $fullname $url; then + gzip -f $fullname || exit 1 + exit 0 + fi + + rm -f $fullname +done + +exit 1 diff --git a/gitcvs.zsh b/gitcvs.zsh new file mode 100755 index 0000000..9cc13d9 --- /dev/null +++ b/gitcvs.zsh @@ -0,0 +1,144 @@ +#!/usr/bin/env zsh +# +# Copyright © 2008-2011 Nick Bowler +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +# List all commits that have not yet been committed to CVS. (run this command +# from the root of the CVS repository) +git-cvs-commitlist() { + if [[ -z "$1" ]]; then + echo "usage: git-cvs-commitlist [origin]" 1>&2 + return 1 + fi + + [[ -z "$2" ]] && 2="remotes/CVS/master" + [[ -d "$1/.git" ]] && 1="$1/.git" + + GIT_DIR="$1" git cherry -v "$2" +} + +# Commit a specified commit to CVS (run this command from the root of the CVS +# repository). +git-cvs-commit() { + if [[ -z "$1" || -z "$2" ]]; then + echo "usage: git-cvs-commit " 1>&2 + return 1 + fi + + [[ -d "$1/.git" ]] && 1="$1/.git" + + GIT_DIR="$1" git cvsexportcommit -cvp "$2" +} + +# Commit all commits that are not yet in CVS to CVS. +# (run this command from the root of the CVS repository). +git-cvs-commit-all() { + if [[ -z "$1" ]]; then + echo "usage: git-cvs-commit-all " 1>&2 + return 1 + fi + + git-cvs-commitlist $1 | while read i; do + local commit=$(echo $i | cut -d ' ' -f 2) + git-cvs-commit $1 $commit || return $? + done +} + +# Import the latest and greatest changes into a git repository (will create +# a new repository if run outside of any git repo). +# +# The details used to fetch from CVS are recorded so that running +# git-cvs-fetch with no arguments will repeat the same fetch as last time. +# Currently, some arguments which don't make sense to record are recorded +# anyway, so you may need to manually edit .git/config afterwards... +git-cvs-fetch() { + local tmp cvsroot cvsmod cvsopts + + cvsroot=${$(git config cvs.root):-$CVSROOT} + cvsopts=${$(git config cvs.opts):-'$@'} + eval "cvsopts=($cvsopts)" + + # Extract a CVS root from opts, if any. + while [[ $((tmp=${cvsopts[(i)-d*]})) -le $#cvsopts ]]; do + cvsroot=${cvsopts[$tmp]#-d} + cvsopts=($cvsopts[1,$tmp-1] $cvsopts[$tmp+1,-1]) + if [[ -z $cvsroot ]]; then + cvsroot=${cvsopts[$tmp]} + cvsopts=($cvsopts[1,$tmp-1] $cvsopts[$tmp+1,-1]) + fi + done + + if ! cvsmod=$(git config cvs.module); then + cvsmod=$cvsopts[-1] + cvsopts=($cvsopts[1,-2]) + fi + + # By this point, we must have a CVS root and module. + if [[ -z $cvsroot ]]; then + printf '%s: no CVS root set.\n' $0 1>&2 + return 1 + fi + + if [[ -z $cvsmod ]]; then + printf '%s: no CVS module set.\n' $0 1>&2 + return 1 + fi + + GIT_DIR=`git rev-parse --git-dir 2>/dev/null` git cvsimport \ + -air CVS -d $cvsroot $cvsopts $cvsmod || return 1 + + # Store the settings for this run. + git config cvs.root $cvsroot + git config cvs.module $cvsmod + [[ $#cvsopts -gt 0 ]] && git config cvs.opts ${(j: :)${(qq)cvsopts}} + : +} + +# Generate an excludes file, suitable for writing to .git/info/exclude, based +# on .cvsignore files found in the repository. Run this command from the git +# repository. +git-cvs-genexclude() { + local dir i j cvsdefaults work + + work=`git rev-parse --show-toplevel` || return 1 + if test -z $work; then + printf '%s: must be run from the work tree\n' $0 1>&2 + return 1 + fi + + printf '#\n# Automatically generated by %s\n#\n' "$0 $*" + + # CVS ignores a number of patterns by default. + cvsdefaults=( + 'RCS' 'SCCS' 'CVS' 'CVS.adm' 'RCSLOG' 'cvslog.*' + 'tags' 'TAGS' '.make.state' '.nse_depinfo' + '*~' '#*' '.#*' ',*' '_$*' '*$' + '*.old' '*.bak' '*.BAK' '*.orig' '*.rej' '.del-*' + '*.a' '*.olb' '*.o' '*.obj' '*.so' '*.exe' + '*.Z' '*.elc' '*.ln' 'core' + ) + printf '%s\n' $cvsdefaults + + find $work -name .cvsignore | while read i; do + dir=${$(dirname $i)#$work} + + < $i while read j; do + printf '%s/%s\n' $dir $j + done + done +} + +# For executable-like invocation... + +# It would be nice if we could detect whether we are invoked as an executable +# and print a usage message if not given any arguments. I have yet to find +# a clean way to do this, however. + +if [ $# -gt 0 ]; then + func="git-cvs-${1#git-cvs-}" + shift + "$func" $@ +fi diff --git a/ircmpc.pl b/ircmpc.pl new file mode 100644 index 0000000..138a516 --- /dev/null +++ b/ircmpc.pl @@ -0,0 +1,134 @@ +# Copyright (C) 2008, 2010 Nick Bowler +# +# Irssi script for annoying your friends with MPD status information. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +use strict; +use vars qw($VERSION %IRSSI); + +use utf8; +use encoding 'utf8'; +use IO::Socket::INET6; + +use Irssi qw( + command_bind + settings_get_bool settings_add_bool + settings_get_int settings_add_int + settings_get_str settings_add_str +); + +$VERSION = '0.1'; +%IRSSI = ( + authors => 'Nick Bowler', + contact => 'nbowler@draconx.ca', + name => 'MPC/Irssi', + description => 'Irssi client for the Music Player Daemon.', + license => 'WTFPL2', +); + +# MPD Connection Settings +$ENV{"MPD_HOST"} = "localhost" unless ($ENV{"MPD_HOST"}); +$ENV{"MPD_PORT"} = 6600 unless ($ENV{"MPD_PORT"}); +settings_add_str("mpd", "mpd_host", $ENV{"MPD_HOST"}); +settings_add_int("mpd", "mpd_port", $ENV{"MPD_PORT"}); + +# Open a connection to MPD. +sub mpd_open { + my $sock = new IO::Socket::INET6( + PeerAddr => settings_get_str("mpd_host"), + PeerPort => settings_get_int("mpd_port"), + Proto => 'tcp', + ) or do { + print CLIENTERROR "failed to open MPD socket: $!."; + return undef; + }; + binmode($sock, ":utf8"); + + # Grab the MPD version. + if (!(<$sock> =~ /^OK MPD ([0-9]+)\.([0-9]+)\.([0-9]+)$/)) { + print CLIENTERROR "failed MPD handshake: $!."; + return undef; + } + + return $sock; +} + +sub mpd_currentsong { + my ($sock) = @_; + my %data; + + # Get MPD status. + print $sock "status\n"; + while (<$sock>) { + last if (/^OK/); + do { print CLIENTERROR "$_"; return undef } if (/^ACK/); + + if (/^(\w+): (.*)$/) { + $data{$1} = $2; + } + } + return %data if (!defined $data{"songid"}); + + # Get song info + print $sock "playlistid $data{songid}\n"; + while (<$sock>) { + last if (/^OK/); + do { print CLIENTERROR "$_"; return undef } if (/^ACK/); + + if (/^(\w+): (.*)$/) { + $data{$1} = $2; + } + } + + return %data; +} + +sub mpd_close { + my ($sock) = @_; + print $sock "close\n"; + close $sock; +} + +sub fileformat { + ($_) = @_; + + return "vorbis" if (/\.ogg$/); + return "flac" if (/\.flac$/); + return "mp3" if (/\.mp3$/); +} + +sub cmd_current { + my ($data, $server, $witem) = @_; + my ($window, $local) = (Irssi::active_win()); + + my $sock = mpd_open() or return; + my %info = mpd_currentsong($sock) or return; + mpd_close($sock); + + if ($info{'state'} eq "stop") { + $window->print("MPD is currently stopped."); + return; + } + + my $text = "$info{Artist} – $info{Title}, " + . "track $info{Track} of “$info{Album}” " + . "($info{bitrate}kbps " . fileformat($info{'file'}) . ")"; + + # If the active window is not a channel or query, display locally. + $local = 1 unless ($witem && ($witem->{type} eq "CHANNEL" + || $witem->{type} eq "QUERY")); + + if ($local) { + $window->print($text); + } elsif ($data) { + $data =~ s/\s.*//; + $witem->command("SAY $data: /me is listening to $text."); + } else { + $witem->command("SAY /me is listening to $text."); + } +} + +command_bind('mpd', 'cmd_current'); diff --git a/libs.sh b/libs.sh new file mode 100755 index 0000000..228e8ee --- /dev/null +++ b/libs.sh @@ -0,0 +1,63 @@ +#!/bin/zsh +# +# Copyright (C) 2009 Nick Bowler +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. +# +# Determine library dependencies for dynamically linked binaries and output +# directives suitable for gen_init_cpio to include all required libraries +# in an initramfs archive. Users of this script will probably need to adjust +# the BASE, LDD and LOADER variables appropriately. + +# Directory in which the libraries are located. +BASE=/usr/x86_64-pc-linux-uclibc + +# How to run ldd. The uClibc dynamic loader explodes wonderfully if +# LD_LIBRARY_PATH is not set correctly. +LDD="LD_LIBRARY_PATH=$BASE/lib:$BASE/usr/lib x86_64-pc-linux-uclibc-ldd" + +# Where the dynamic loader is located. Always included in the output. +LOADER="/lib/ld64-uClibc.so.0" + +getlibs() { + local file=$1 + + if test -u "$1" -o -g "$1"; then + file=`mktemp` + cp -f "$1" "$file" + fi + + eval "$LDD '$file'" | sed -n 's/.*=> \(.*\) (0x[[:xdigit:]]*)/\1/p' + + if [ "$file" != "$1" ]; then + rm -f "$file" + fi +} + +libs=(`for i in "$@"; do getlibs "$i"; done | sort -u`) + +output="file $LOADER $BASE$LOADER 0755 0 0" + +while [ $#libs -gt 0 ]; do + i=${libs[1]} + libs=(${libs[2,-1]}) + + if ! [[ "$i" =~ "^$BASE" ]]; then continue; fi + + dest=${i#$BASE} + + if test -L "$i"; then + output="$output\nslink $dest $(readlink "$i") 0644 0 0" + next=`readlink "$i"` + if [ "$next[0]" != "/" ]; then + next="$(dirname "$i")/$next" + fi + libs+=$next + elif test -f "$i"; then + output="$output\nfile $dest $i 0644 0 0" + fi +done + +echo "$output" | sort -u diff --git a/maildir-clean.zsh b/maildir-clean.zsh new file mode 100755 index 0000000..ee6438b --- /dev/null +++ b/maildir-clean.zsh @@ -0,0 +1,60 @@ +#!/usr/bin/env zsh +# +# Copyright (C) 2009,2011 Nick Bowler +# +# Delete email older than a given date from a maildir. Assumes that the +# Date headers in the mails are valid and correct, and is therefore +# somewhat less useful for cleaning up spam. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +cutoff='last week' +maildir= + +while getopts 'd:' opt $@; do + case $opt in + d) cutoff=$OPTARG ;; + \?) exit 1 ;; + esac +done + +shift $((OPTIND-1)) + +# Determine maildir +if [[ -n $1 ]]; then + maildir=$1 +elif [[ -n $MAIL ]]; then + maildir=$MAIL +fi + +# Validate arguments +date=`date --rfc-3339=date --date=$cutoff` || exit 1 + +if [[ -z $maildir ]]; then + printf '%s: you must specify a maildir.\n' $0 + exit 1 +fi + +if ! [[ -d $maildir/cur && -d $maildir/new && -d $maildir/tmp ]]; then + printf '%s: %s is not a valid maildir.\n' $0 $maildir + exit 1 +fi + +total=`ls $maildir/cur | wc -l`. +count=0 + +for i in $maildir/cur/*; do + datehdr=`formail -x Date < $i` + maildate=`date --rfc-3339=date --date=$datehdr` + + count=$((count+1)) + + if [[ $maildate < $date ]]; then + printf '(%.0f%%) %s: deleting %s\n' $((100*count/total)) $0 $i + rm -f $i + else + printf '(%.0f%%) %s: keeping %s\n' $((100*count/total)) $0 $i + fi +done diff --git a/modules.zsh b/modules.zsh new file mode 100755 index 0000000..6115d27 --- /dev/null +++ b/modules.zsh @@ -0,0 +1,64 @@ +#!/usr/bin/env zsh +# +# Copyright © 2009 Nick Bowler +# +# Generate module listings for an initramfs by searching a subset of the +# installed modules for a particular kernel version. Currently, all +# filesystem and MD drivers, plus all their (recursive) dependencies, +# are included. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +usage() { + echo "usage: modules.zsh kernel-version" 1>&2 + exit 1 +} + +out_mkdir_p() { + local i base path + base=$1 + path=(${(s./.)2}) + + for i in {1..$#path}; do + echo "dir $base/${(j./.)path[1,i]} 0755 0 0" + done +} + +if [ -z "$1" ]; then + usage +fi + +kver=$1 + +moduledir=/lib/modules/$kver + +if ! [ -d "$moduledir" ]; then + echo "module directory for kernel \`\`$kver'' does not exist." 1>&2 + usage +fi + +kmods=(`find $moduledir/kernel/fs $moduledir/kernel/drivers/md \ + -type f -name '*.ko' -exec basename {} .ko \;`) + +paths=() + +while [ $#kmods -gt 0 ]; do + i=${kmods[1]} + kmods=(${kmods[2,-1]}) + + info=`modinfo -k $kver $i` + paths+=`echo $info | sed -n 's/^filename:[[:space:]]*//p'` + + kmods+=(${(s.,.)$(echo $info | sed -n 's/^depends:[[:space:]]*//p')}) +done + +echo "dir $moduledir 0755 0 0" +echo "file $moduledir/modules.dep $moduledir/modules.dep 0644 0 0" +echo "file $moduledir/modules.alias $moduledir/modules.alias 0644 0 0" +for i in $paths; do + i=${i#$moduledir/} + out_mkdir_p $moduledir $(dirname $i) + echo "file $moduledir/$i $moduledir/$i 0644 0 0" +done | sort -u diff --git a/mpdbetter.sed b/mpdbetter.sed new file mode 100755 index 0000000..a59a4f8 --- /dev/null +++ b/mpdbetter.sed @@ -0,0 +1,52 @@ +#!/bin/sed -f + +# Nick's mpdbetter `configuration file'. Input a list of track titles, and +# substitute each with the title of the best version of that song. + +# Handle special cases first +/ -extra hot mix-/{s///;b} +/ -Raven fly edit-/{s///;b} +/ -2006 memorial mix-/{s///;b} +/ -Practice Track-/{s///;b} +/ On Air Ver\./{s///;b} +/-\[instrumental\]/{s///;b} +/ちいさなヘミソフィア/{s//ヘミソフィア/;b} +/愛のメロディー/{s/\((.*)\)\?$/(soundtrack ver.)/;b} +/COLORS -.*/{s//COLORS/;b} +/ without NATSUMI/{s///;b} + +# Some literal replacements +/松澤由美 - SNOW/c\ +島みやえい子 - SNOW -Album Mix- +/詩月カオリ - Senecio/c\ +詩月カオリ - Senecio -Album Mix- +/AKI - Pure Heart〜世界で一番アナタが好き〜/c\ +詩月カオリ - Pure Heart 〜世界で一番アナタが好き〜 -Remix- +/open〜オルゴールバージョン〜/c\ +詩月カオリ - open +/euphoric field/c\ +ELISA - euphoric field (Japanese) +/La Clef 〜迷宮の鍵〜/c\ +KOTOKO - La Clef 〜迷宮の鍵〜 + +# Sanitization +s/「\(The Sore Feet Song\)」/\1/ + +# General rules get most tracks +/\s*[~〜<〈[(ー–−-]\s\?inst.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?tv.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?without.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?w\/o.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?off.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?back.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?short.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?karaoke.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?game.*/I {s///;b} +/\s*[~〜<〈[(ー–−-]\s\?[^ ]*カラオケ.*/I {s///;b} + +# When we lack brackets we match more specific strings +/ instrumental.*/I {s///;b} +/ off vocal.*/I {s///;b} +/ short.*/I {s///;b} +/ tv-\w\+/I {s///;b} +/ anime ver\./I {s///;b} diff --git a/mpdscripts.sh b/mpdscripts.sh new file mode 100755 index 0000000..ad390f3 --- /dev/null +++ b/mpdscripts.sh @@ -0,0 +1,239 @@ +#!/bin/zsh +# +# Collection of shell functions for manipulating MPD. Functions prefixed with +# __ are intended for use in these scripts, whereas functions prefixed with +# "mpd" are intended to be called by users. +# +# This file may be sourced by your shell startup scripts in order to load all +# the functions into the environment, or it may be invoked as an executable. +# The first argument to the script designates the function, with or without +# the leading "mpd". Subsequent arguments are passed to the called function. +# +# For example, you might run: +# `source mpdscripts.sh' followed by `mpdlist mycoolsong' +# or equivalently: +# `./mpdscripts.sh list mycoolsong'. +# The latter form is useful when not invoking the scripts from a shell. +# +# This version specifies the functions: +# mpdnext, mpdbetter, mpdlist, mpdalbum, and mpdshow. +# +# See the comments above each function for more details. +# +# Requires mpc, GNU grep and GNU sed. +# Works with zsh or bash. +# +# Send questions or comments to nbowler@draconx.ca, or find me on IRC as +# Draconx on irc.freenode.net. +# +# Licensed under the terms of the Do What The Fuck You Want To Public License. +# +############################################################################## + +__mpd_playlist() +{ + mpc --format='%position%) %artist% - %title%' playlist +} + +# __mpd_trackid [regexp] +# With a regular expression as an argument, search the MPD playlist for the +# track matching the expression and print the index of that track. Otherwise, +# print the index of the currently playing track. +__mpd_trackid() +{ + local id + + if [ -z "$1" ]; then + id=`mpc | sed -n "2{s/.*#//;s/\/.*//p}"` + else + id=`__mpd_playlist | sed -n \ + "/${1//\//\\\/}/I{s/^[^[:digit:]]//;s/).*//p;q}"` + fi + + [ -z "$id" ] && return 1 + + echo "$id" + return 0 +} + +# __mpd_trackname [id] +# With a track ID as an argument, print the title of that track. Otherwise, +# print the title of the currently playing track. +__mpd_trackname() +{ + local title + + if [ -z "$1" ]; then + title=`mpc | sed -n "1h;2{g;p;q}"` + else + title=`__mpd_playlist | sed -n \ + "/^[^[:digit:]]*$1)/s/^.[0-9]*) //p"` + fi + + [ -z "$title" ] && return 1 + + echo "$title" + return 0 +} + +# mpdnext [advance] +# Searches the playlist for a track matching , then moves it to the +# position following the currently playing track. If [advance] is a non-empty +# string, also run `mpc next' to play the moved track immediately. +mpdnext() +{ + local index target name + + if [ -z "$1" ]; then + echo "usage: mpdnext " + return 1 + fi + + if ! index=`__mpd_trackid`; then + echo "mpdnext: MPD is currently stopped." + return 1 + fi + + if ! target=`__mpd_trackid "$1"`; then + echo "mpdnext: target not found." + return 1 + fi + + name=`__mpd_trackname $((target))` + + if [ $target -lt $index ]; then + mpc move $target $index + elif [ $target -gt $index ]; then + mpc move $target $((index+1)) + else + echo "mpdnext: selected the playing track" + return 1 + fi + + echo "mpdnext: $name moved." + + # Play track if desired + [ -n "$2" ] && mpc next + + return 0 +} + +# mpdbetter [sedscript] +# Processes the title of the currently playing song through the sed program +# specified by [sedscript] (or ~/.mpdbetter if not specified). If the +# resulting title is different, attempt to find it in the playlist and use +# mpdnext to play it immediately. +mpdbetter() +{ + local title better script="$HOME/.mpdbetter" + [ -n "$1" ] && script="$1" + + if ! title=`__mpd_trackname`; then + echo "mpdbetter: MPD is currently stopped." + return 1 + fi + + if ! better=`echo "$title" | sed -f "$script"`; then + echo "mpdbetter: error in script: \`$script'" + return 1 + fi + + if [ "$better" = "$title" ]; then + echo "mpdbetter: nothing to be done." + return 1 + fi + + mpdnext ") $better\$" next +} + +# mpdlist [regexp] +# Simply search the playlist for tracks matching [regexp], or print the entire +# playlist if [regexp] is not specified. +mpdlist() +{ + if [ -z "$1" ]; then + __mpd_playlist + else + __mpd_playlist | grep --color=auto -i -- "$1" + fi +} + +# mpdshow +# Display the currently playing track as well as the part of the playlist +# surrounding that track. +mpdshow() +{ + local index + + if ! index=`__mpd_trackid`; then + echo "mpdshow: MPD is currently stopped." + return 1 + fi + + mpc + echo + __mpd_playlist | grep -C 5 --color=auto "^$index)" +} + +# mpdalbum +# Search the playlist for tracks belonging to an album matching . +# Print those tracks grouped by album and sorted by track number. +mpdalbum() +{ + local current list + + if [ -z "$1" ]; then + echo "usage: mpdalbum " + return 1 + fi + + # This horrible hack grabs the relevant playlist entries and separates + # desired fields with @!@ and !@!. + list="`mpc --format $'\n%album%\n%track%\n%artist% - %title%' playlist \ + | sed -n " + /^.[0-9]*) $/{ + h;n + /${1//\//\\\/}/I{ + x;G;s/$/@!@/ + N;s/$/!@!/ + N;s/\n//g + p + } + } + " + `" + +# list="`mpc --format '%album%@!@%track%!@!%artist% - %title%' playlist |\ +# sed -n 'h;s/@!@.*//;'"/${1//\//\\\/}/I{g;p}" | sort -k 2`" + + if [ -z "$list" ]; then + echo "mpdalbum: no matches for \`$1'" + return 1 + fi + + echo "$list" | while read i; do + local album="`echo $i | sed 's/.[0-9]*) \(.*\)@!@.*/\1/'`" + local index="`echo $i | sed 's/[# >]\?\([0-9]*\)).*/\1/'`" + local title="`echo $i | sed 's/.*!@!//'`" + + if [ "$album" != "$current" ]; then + [ -n "$current" ] && echo + echo "$album" + current="$album" + fi + + echo -e " $index)\t$title" + done +} + +# For executable-like invocation... + +# It would be nice if we could detect whether we are invoked as an executable +# and print a usage message if not given any arguments. I have yet to find +# a clean way to do this, however. + +if [ $# -gt 0 ]; then + func="mpd${1#mpd}" + shift + "$func" $@ +fi diff --git a/mplus-t-gen.py b/mplus-t-gen.py new file mode 100755 index 0000000..d95702a --- /dev/null +++ b/mplus-t-gen.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# coding=utf-8 +# +# Copyright © 2011 Nick Bowler +# +# Generates new M+ fonts with the CJK characters enlarged, based on the IPAG +# versions. This gives better proportions relative to DejaVu Sans Mono in +# rxvt-unicode. +# +# Run this script in a directory containing the M+ fonts. The new fonts will +# be called M+1T+IPAG and M+2T+IPAG. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +import fontforge as ff +import psMat +import re + +families = [ "M+1M+IPAG", "M+2M+IPAG" ] +scripts = [ "hani", "kana" ] + +for family in families: + font = ff.open(family + ".ttf") + + font.familyname = re.sub(r"(\d)M", r"\1T", font.familyname) + font.fontname = font.familyname + "-Regular" + font.fullname = font.familyname + " Regular" + + for glyph in font.selection.all().byGlyphs: + for s in scripts: + if (glyph.script == s): + glyph.transform(psMat.scale(1.35)) + + print "Generating " + font.familyname + ".ttf" + font.generate(font.familyname + ".ttf") + font.close() diff --git a/musicstats.zsh b/musicstats.zsh new file mode 100755 index 0000000..c5b9a95 --- /dev/null +++ b/musicstats.zsh @@ -0,0 +1,61 @@ +#!/usr/bin/env zsh +# +# Copyright © 2009 Nick Bowler +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +sizes=0 +while getopts 's' opt $@; do + case $opt in + s) sizes=1 ;; + \?) exit 1 ;; + esac +done + +shift $((OPTIND-1)) + +if [[ $# == 0 ]]; then + printf 'usage: %s [-s] directory [directory ...]\n' $0 + exit 1 +fi + +alias mfind='find $@ -type f' +flac=`mfind -name '*.flac' | wc -l` +vorbis=`mfind -name '*.ogg' | wc -l` +mp3=`mfind -name '*.mp3' | wc -l` +total=$((flac+vorbis+mp3)) + +if [[ $total == 0 ]]; then + printf '0 tracks.\n' + exit +fi + +printf '%d tracks: %d (%.1f%%) flac, %d (%.1f%%) vorbis, %d (%.1f%%) mp3.\n' \ + $total \ + $flac $((100.0*flac /total)) \ + $vorbis $((100.0*vorbis/total)) \ + $mp3 $((100.0*mp3 /total)) + +if [[ $sizes == 1 ]]; then + alias getsize="du -mc --files0-from=- | sed -n '\$s/\ttotal$//p'" + float totalsize=`mfind -print0 | getsize` + float flacsize=`mfind -name '*.flac' -print0 | getsize` + float vorbissize=`mfind -name '*.ogg' -print0 | getsize` + float mp3size=`mfind -name '*.mp3' -print0 | getsize` + + if [[ $totalsize -gt 4000 ]]; then + printf 'size: %.1fG total, %.1fG flac, %.1fG vorbis, %.1fG mp3.\n' \ + $((totalsize/1024)) \ + $((flacsize/1024)) \ + $((vorbissize/1024)) \ + $((mp3size/1024)) + else + printf 'size: %.1fM total, %.1fM flac, %.1fM vorbis, %.1fM mp3.\n' \ + $((totalsize)) \ + $((flacsize)) \ + $((vorbissize)) \ + $((mp3size)) + fi +fi diff --git a/openexec.c b/openexec.c new file mode 100644 index 0000000..8cce5f9 --- /dev/null +++ b/openexec.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2011 Nick Bowler + * + * The Mutt email client opens mail attachments with external programs + * synchronously: the attachment is decoded and saved to /tmp, the filename + * is passed to the program to be run, and when the program exits, the file + * is unlinked. This leads to an obvious problem: if the attachment-viewing + * program runs in the foreground, one cannot interact with mutt until you + * quit it. If the program runs in the background, then there is a race + * between the program opening the file and mutt unlinking it. + * + * This tool solves this problem by opening all files and spawning a program + * in the background. It is safe to unlink the files after they are opened, + * so the race with mutt is avoided. + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +static const char *progname = "openexec"; +static const char sopts[] = "-VH"; +static const struct option lopts[] = { + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'H' }, + { 0 } +}; + +static void print_version(void) +{ + puts("openexec version 1.0"); + puts("\ +Copyright (C) 2011 Nick Bowler.\n\ +License WTFPL2: Do What The Fuck You Want To Public License, version 2.\n\ +This is free software: you are free to do what the fuck you want to.\n\ +There is NO WARRANTY, to the extent permitted by law.\ + "); +} + +static void print_usage(FILE *f) +{ + fprintf(f, "Usage: %s [options] [file ...] -- program [arguments]\n", + progname); +} + +static void print_help(void) +{ + print_usage(stdout); + puts("Detailed help coming eventually."); +} + +static char *fake_open(const char *filename) +{ + char *buf; + int n, fd; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "%s: %s: %s\n", progname, filename, + strerror(errno)); + return NULL; + } + + n = snprintf(NULL, 0, "/proc/self/fd/%d", fd); + buf = malloc(n+1); + if (!buf) { + close(fd); + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return NULL; + } + + sprintf(buf, "/proc/self/fd/%d", fd); + return buf; +} + +int main(int argc, char **argv) +{ + int nargs = 0, opt, rc, err, fds[2]; + + if (argc > 0) + progname = argv[0]; + + char *args[argc]; + + while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { + switch (opt) { + case 1: + args[nargs] = fake_open(optarg); + if (!args[nargs]) + return EXIT_FAILURE; + nargs++; + break; + case 'V': + print_version(); + return EXIT_SUCCESS; + case 'H': + print_help(); + return EXIT_SUCCESS; + default: + print_usage(stderr); + return EXIT_FAILURE; + } + } + + if (!argv[optind]) { + print_usage(stderr); + return EXIT_FAILURE; + } + + args[nargs] = NULL; + memmove(args + (argc - optind), args, (nargs + 1) * sizeof *args); + memcpy(args, argv + optind, (argc - optind) * sizeof *args); + + /* + * We use a close-on-exec pipe for the child to signal success/failure + * to the parent. On successful exec, the write end of the pipe will + * be closed, causing the read in the parent to return 0. On failure, + * errno will be written to the pipe, allowing the parent to print + * an error message. + */ + rc = pipe2(fds, O_CLOEXEC); + if (rc == -1) { + fprintf(stderr, "%s: pipe2: %s\n", progname, strerror(errno)); + return EXIT_FAILURE; + } + + switch (fork()) { + case -1: + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return EXIT_FAILURE; + case 0: + execvp(args[0], args); + + /* This horrible if statement shuts up GCC... */ + err = errno; + if (write(fds[1], &err, sizeof err)) + ; + _Exit(EXIT_FAILURE); + } + + close(fds[1]); + rc = read(fds[0], &err, sizeof err); + if (rc == -1) { + /* No information about the child's state. */ + fprintf(stderr, "%s: read: %s\n", progname, strerror(errno)); + return EXIT_FAILURE; + } else if (rc == 0) { + /* Exec was successful, so we are too. */ + return EXIT_SUCCESS; + } + + /* Exec in the child failed, print the error we received on the pipe. */ + fprintf(stderr, "%s: %s: %s\n", progname, args[0], strerror(err)); + return EXIT_FAILURE; +} diff --git a/polya.tex b/polya.tex new file mode 100644 index 0000000..4a32eb9 --- /dev/null +++ b/polya.tex @@ -0,0 +1,72 @@ +\documentclass{article} + +\usepackage{fullpage} +\usepackage{amssymb} +\usepackage{tikz} + +% Peano Curve of Polya +\newcounter{polyax} +\newcounter{polyay} +\newcounter{polyaz} +\newcounter{polyap} + +\newcommand{\polyafig}[3]{ + \coordinate (p0) at (#1); + \coordinate (p1) at +(0:#2); + \coordinate (p2) at (intersection cs: + first line={(p0)--+(60:1)}, + second line={(p1)--($(p1)!1!150:(p0)$)}); + \draw (p0) -- (p1) -- (p2) --cycle; + + \coordinate (p3) at ($(p0)!(p2)!(p1)$); + \draw[densely dotted] (p2) -- (p3); + + \setcounter{polyax}{0} + \setcounter{polyay}{2} + \setcounter{polyaz}{1} + \setcounter{polyap}{3} + + \foreach \d in {#3} { + \pgfmathtruncatemacro{\x}{\thepolyax} + \pgfmathtruncatemacro{\y}{\thepolyay} + \pgfmathtruncatemacro{\z}{\thepolyaz} + + \ifthenelse{\equal{\d}{1}}{ + \pgfmathtruncatemacro{\xx}{\y} + \pgfmathtruncatemacro{\zz}{\z} + }{ + \pgfmathtruncatemacro{\xx}{\x} + \pgfmathtruncatemacro{\zz}{\y} + } + \pgfmathtruncatemacro{\yy}{\y+1} + + \pgfmathtruncatemacro{\v}{\yy+1} + \coordinate (p\v) at + ($(p\xx)!(p\yy)!(p\zz)$); + \draw[densely dotted] (p\yy) -- (p\v); + + \setcounter{polyax}{\xx} + \setcounter{polyay}{\yy} + \setcounter{polyaz}{\zz} + \setcounter{polyap}{\v} + } +} + +\title{The Peano Curve of P\'olya} +\author{} +\date{} + +\begin{document} +\maketitle + +A \emph{Peano Curve} is a curve which hits every point in a two-dimensional +object. Recall that a curve is a continuous function +\[ +f : [0,1] \to \mathbb{R}^2. +\] +A famous example of such a curve is due to George P\'olya, which fills a +right-angled triangle. For $x \in [0,1)$, let $\{a_n\}^\infty_{n=0}$ be the +sequence of digits after the decimal point in its binary expansion. If there +are multiple binary expansions, either will do (the result is the same). + +\end{document} diff --git a/quine.hs b/quine.hs new file mode 100644 index 0000000..e75a8f9 --- /dev/null +++ b/quine.hs @@ -0,0 +1,2 @@ +listing = "main = putStr \"listing = \" >> print listing >> putStrLn listing" +main = putStr "listing = " >> print listing >> putStrLn listing diff --git a/quine.sed b/quine.sed new file mode 100755 index 0000000..af00345 --- /dev/null +++ b/quine.sed @@ -0,0 +1,28 @@ +#!/bin/sed -nf +# A quine for GNU sed. +s:.*: i\\\ + #!/bin/sed -nf\\\ + # A quine for GNU sed.\ + h\ + s/\\\\/\\\\\\\\/g\ + s/\&/\\\\\&/g\ + s/\:/\\\\\:/g\ + s/\\n/\\\\\\n/g\ + s/.*/s\:.*\:\&\:/\ + p\ + g\ + s/ \\{2,\\}//g\ + p: +i\ +#!/bin/sed -nf\ +# A quine for GNU sed. +h +s/\\/\\\\/g +s/&/\\&/g +s/:/\\:/g +s/\n/\\\n/g +s/.*/s:.*:&:/ +p +g +s/ \{2,\}//g +p diff --git a/replaygain.zsh b/replaygain.zsh new file mode 100755 index 0000000..89b293f --- /dev/null +++ b/replaygain.zsh @@ -0,0 +1,64 @@ +#!/usr/bin/env zsh +# +# Copyright (C) 2009 Nick Bowler +# +# Adds replaygain tags to the flac files in a directory. Assumes that all +# the tracks from a CD are located in the same directory. Unless the -f flag +# is specified, directories already containing replaygain tags will not be +# processed. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +alias flactags='metaflac --export-tags-to=-' + +is_disc_dir() { + local files + + [[ -d $1 ]] || return 1 + + files=($1/*.flac(N)) + [[ $#files > 0 ]] || return 1 +} + +has_replay_gain() { + local files + + files=($1/*.flac(N)) + for i in $files; do + (flactags $i | grep -q REPLAYGAIN_REFERENCE) || return 1 + done +} + +force=0 +while getopts 'f' opt $@; do + case $opt in + f) force=1 ;; + \?) exit 1 ;; + esac +done + +shift $((OPTIND-1)) + +if [[ $# == 0 ]]; then + printf 'usage: %s [-f] directory [directory ...]\n' $0 + exit 1 +fi + +for dir in $@; do + printf '%s: ' $dir + + if ! is_disc_dir $dir; then + printf 'not an album directory, skipping.\n' + continue + fi + + if [[ $force == 0 ]] && has_replay_gain $dir; then + printf 'already done, skipping.\n' + continue + fi + + metaflac --add-replay-gain $dir/*.flac + printf 'done.\n' +done diff --git a/runcron.zsh b/runcron.zsh new file mode 100755 index 0000000..8248728 --- /dev/null +++ b/runcron.zsh @@ -0,0 +1,24 @@ +#!/usr/bin/env zsh +# +# Copyright © 2011 Nick Bowler +# +# Simple script to dispatch jobs in /etc/cron.whatever. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +crondir=/etc/cron.$1 + +if ! [[ -d $crondir ]]; then + printf '%s: argument must be one of:' $0 + for i in /etc/cron.*; do + if ! [[ -d $i ]]; then continue; fi + printf ' %s' ${i#/etc/cron.} + done + echo . + + exit 1 +fi + +run-parts --verbose --regex='^[^.][[:alnum:]_.-]+$' $crondir diff --git a/spamassassin.zsh b/spamassassin.zsh new file mode 100755 index 0000000..becf6b6 --- /dev/null +++ b/spamassassin.zsh @@ -0,0 +1,29 @@ +#!/usr/bin/env zsh +# +# Copyright © 2011 Nick Bowler +# +# Check mail for spam, automatically spawning spamd as necessary. This +# script can therefore be used in a procmail recipe to efficiently handle +# spam filtering. +# +# Runs plain old spamassassin if the daemon cannot be started for any reason. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +spamd_sock=$HOME/.spamd +spamd_log=$HOME/.spamdlog +spamd_pid=$HOME/.spamdpid +spamd=/usr/sbin/spamd + +run_spamd() { + flock -n $spamd_pid $spamd --pidfile=$spamd_pid --syslog=$spamd_log \ + --socketpath=$spamd_sock $@ +} + +if ! spamc -K -U $spamd_sock &>/dev/null; then + run_spamd --daemonize || exec spamassassin +fi + +exec spamc -U $spamd_sock