]> snippets.scripts.mit.edu Git - Scripts/git/.git/commitdiff
Merge remote branch 'django-mit/master'
authorAlex Dehnert <adehnert@mit.edu>
Mon, 6 Jun 2011 06:37:44 +0000 (02:37 -0400)
committerAlex Dehnert <adehnert@mit.edu>
Mon, 6 Jun 2011 06:37:44 +0000 (02:37 -0400)
29 files changed:
.gitignore [new file with mode: 0644]
README [new file with mode: 0644]
TracZephyrPlugin/.gitignore [new file with mode: 0644]
TracZephyrPlugin/COPYING [new file with mode: 0644]
TracZephyrPlugin/INSTALL [new file with mode: 0644]
TracZephyrPlugin/ZephyrPlugin.py [new file with mode: 0644]
TracZephyrPlugin/setup.py [new file with mode: 0755]
apt-zephyr/90zephyr [new file with mode: 0644]
apt-zephyr/README [new file with mode: 0644]
apt-zephyr/apt-zephyr [new file with mode: 0755]
barn-growl/abstfilter.py [new file with mode: 0755]
barn-growl/barn-growl.py [new file with mode: 0755]
barn-growl/sexpr.py [new file with mode: 0644]
barnowl/personal-colors.pl [new file with mode: 0644]
barnowl/zcrypt.pl [new file with mode: 0644]
bash/newline-fix.bash [new file with mode: 0644]
certs/pkcs2pem [new file with mode: 0755]
crondiff/crondiff.pl [new file with mode: 0644]
git-hooks/repo-noroot-update [new file with mode: 0755]
git-hooks/zephyr-post-receive [new file with mode: 0755]
git-rcsimport [new file with mode: 0755]
iptables-nat.sh [new file with mode: 0644]
kerberos/kdo [new file with mode: 0644]
kerberos/krbroot [new file with mode: 0755]
programming/disasm [new file with mode: 0755]
programming/gccrun [new file with mode: 0755]
rt/zephyr-scrip.pl [new file with mode: 0644]
svn-hooks/commit-zephyr [new file with mode: 0755]
svn-hooks/zephyr-post-revprop-change [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f3d74a9
--- /dev/null
@@ -0,0 +1,2 @@
+*.pyc
+*~
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..64c8f29
--- /dev/null
+++ b/README
@@ -0,0 +1,22 @@
+This is a repository for useful small snippets that really belong in version
+control in a central spot, instead of someone's Public. Please add stuff
+here via git:
+
+add git
+git clone /mit/snippets
+cd snippets
+[create or copy a file, let's call it "item"]
+git commit item
+git push
+
+If you're not on gsipb and want to commit, ask geofft (or someone on
+sipb-acl) to add you to snippets-committers. You can't write to
+/mit/snippets directly, but if you push (to the master branch) from your
+own checkout, it will automatically update this checkout.
+
+There's a (currently very simple) web interface at
+http://snippets.scripts.mit.edu/
+
+snippets has support for submodules, if things outgrow this repository.
+You can use 'git submodule update --init' to populate them if you want,
+or look at .gitmodules to find where they live now.
diff --git a/TracZephyrPlugin/.gitignore b/TracZephyrPlugin/.gitignore
new file mode 100644 (file)
index 0000000..f07a57d
--- /dev/null
@@ -0,0 +1,4 @@
+build
+dist
+*.pyc
+*.egg-info
diff --git a/TracZephyrPlugin/COPYING b/TracZephyrPlugin/COPYING
new file mode 100644 (file)
index 0000000..c8e2113
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (C) 2011 by Evan Broder, Geoffrey Thomas, Anders Kaseorg,
+Jonathan Reed
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/TracZephyrPlugin/INSTALL b/TracZephyrPlugin/INSTALL
new file mode 100644 (file)
index 0000000..28c6bf2
--- /dev/null
@@ -0,0 +1,14 @@
+To install the TracZephyrPlugin, first run
+
+  $ python setup.py bdist_egg
+
+then copy the .egg file in the dist/ directory into the plugins/ directory of
+your trac install.
+
+To enable the plugin, you must configure the class that zephyr updates should
+go to. To do this, add a section like the following to your trac.ini:
+
+  [ZephyrPlugin]
+  class = debathena
+
+Then be sure to restart the FastCGI processes if there are any.
diff --git a/TracZephyrPlugin/ZephyrPlugin.py b/TracZephyrPlugin/ZephyrPlugin.py
new file mode 100644 (file)
index 0000000..673c339
--- /dev/null
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+
+from trac.core import *
+from trac.ticket import ITicketChangeListener
+import subprocess
+import re
+import textwrap
+import shlex
+
+quoted_re = re.compile('^(?:> ?\n)*> .+\n(?:>(?: .*)?\n)*', re.MULTILINE)
+
+class ZephyrPlugin(Component):
+    implements(ITicketChangeListener)
+    
+    def zwrite(self, id, message):
+        zclass = self.config.get('ZephyrPlugin', 'class')
+        if zclass == '':
+            return
+        command = shlex.split(self.config.get('ZephyrPlugin', 'command').encode('utf-8'))
+        if not command:
+            command = ['zwrite', '-q', '-l', '-d']
+        opcode = self.config.get('ZephyrPlugin', 'opcode')
+        if opcode:
+            command += ['-O', opcode]
+        signature = self.config.get('ZephyrPlugin', 'signature')
+        if signature:
+            command += ['-s', signature]
+        p = subprocess.Popen(command +
+                             ['-c', zclass,
+                              '-i', 'trac-#%s' % id],
+                             stdin=subprocess.PIPE)
+        p.stdin.write(message.replace('@', '@@').encode('utf-8', 'replace'))
+        p.stdin.close()
+        p.wait()
+
+    def format_text(self, text):
+        text = re.sub(quoted_re, u'> […]\n', text)
+        lines = textwrap.fill(text).split('\n')
+        if len(lines) > 5:
+            lines = lines[:5] + [u'[…]']
+        return '\n'.join(lines)
+    
+    def ticket_created(self, ticket):
+        ttype='ticket'
+        if ticket['type'] != 'defect':
+            ttype=ticket['type']
+        message = "%s filed a new %s %s:\n%s\n\n%s" % (ticket['reporter'],
+                                                           ticket['priority'],
+                                                           ttype,
+                                                           ticket['summary'],
+                                                           self.format_text(ticket['description']))
+        self.zwrite(ticket.id, message)
+    
+    def ticket_changed(self, ticket, comment, author, old_values):
+        message = "(%s)\n" % ticket['summary']
+        for field in ticket.fields:
+            name = field['name']
+            if name not in old_values:
+                pass
+            elif field['type'] == 'textarea':
+                message += "%s changed %s to:\n%s\n" % (author, name, self.format_text(ticket[name]))
+            elif ticket[name] and old_values[name]:
+                message += "%s changed %s from %s to %s.\n" % (author, name, old_values[name], ticket[name])
+            elif ticket[name]:
+                message += "%s set %s to %s.\n" % (author, name, ticket[name])
+            elif old_values[name]:
+                message += "%s deleted %s.\n" % (author, name)
+            else:
+                message += "%s changed %s.\n" % (author, name)
+        if comment:
+            message += "%s commented:\n%s\n" % (author, self.format_text(comment))
+        self.zwrite(ticket.id, message)
+    
+    def ticket_deleted(self, ticket):
+        message = "%s deleted ticket %d" % (author, ticket.id)
+        self.zwrite(ticket.id, message)
diff --git a/TracZephyrPlugin/setup.py b/TracZephyrPlugin/setup.py
new file mode 100755 (executable)
index 0000000..67634c7
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+from setuptools import find_packages, setup
+
+setup(
+    name='TracZephyrPlugin',
+    version='1.4.1',
+    author='Evan Broder',
+    author_email='broder@mit.edu',
+    description='Send a zephyr when a Trac ticket is created or updated',
+    py_modules=["ZephyrPlugin"],
+    entry_points = """
+        [trac.plugins]
+        ZephyrPlugin = ZephyrPlugin
+    """,
+)
diff --git a/apt-zephyr/90zephyr b/apt-zephyr/90zephyr
new file mode 100644 (file)
index 0000000..563e792
--- /dev/null
@@ -0,0 +1,3 @@
+DPkg::Pre-Install-Pkgs {"/usr/local/sbin/apt-zephyr --pre-install-pkgs || :";};
+DPkg::Post-Invoke {"/usr/local/sbin/apt-zephyr --post-invoke || :";};
+DPkg::Tools::options::/usr/local/sbin/apt-zephyr::Version "2";
diff --git a/apt-zephyr/README b/apt-zephyr/README
new file mode 100644 (file)
index 0000000..fc20d47
--- /dev/null
@@ -0,0 +1,17 @@
+apt-zephyr hook
+
+Author: Anders Kaseorg <andersk@mit.edu>
+
+Usage: install these files to
+
+/etc/apt/apt.conf.d/90zephyr
+/usr/local/sbin/apt-zephyr
+
+and configure /etc/apt-zephyr.conf, e.g.
+
+CLASS="linerva"
+INSTANCE="apt.$(hostname)"
+
+Valid options include CLASS, INSTANCE, REALM, RECIPIENTS, SIG (default 
+"$(hostname -f)"), OPCODE (default "auto"), ZAUTH (default "", set 
+nonempty to enable).
diff --git a/apt-zephyr/apt-zephyr b/apt-zephyr/apt-zephyr
new file mode 100755 (executable)
index 0000000..fdf1318
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+set -e
+
+# Defaults
+CLASS=
+INSTANCE=
+REALM=
+RECIPIENTS=
+SIG="$(hostname -f)"
+OPCODE=auto
+ZAUTH=
+
+# Read configuration
+. /etc/apt-zephyr.conf
+
+send_zephyr ()
+{
+    zwrite \
+       ${CLASS:+-c "$CLASS"} \
+       ${INSTANCE:+-i "$INSTANCE"} \
+       ${REALM:+-r "$REALM"} \
+       ${SIG:+-s "$SIG"} \
+       ${OPCODE:+-O "$OPCODE"} \
+       ${ZAUTH:--d} \
+       ${RECIPIENTS:+$RECIPIENTS}
+}
+
+package ()
+{
+    package=$1
+    oldver=$2
+    cmp=$3
+    newver=$4
+
+    if [ "$newver" = '-' ] && [ "$oldver" = '-' ]; then
+       echo "Purging $package"
+    elif [ "$newver" = '-' ]; then
+       echo "Removing $package $oldver"
+    elif [ "$oldver" = '-' ]; then
+       echo "Installing $package $newver"
+    elif [ "$cmp" = '<' ]; then
+       echo "Upgrading $package $oldver to $newver"
+    elif [ "$cmp" = '=' ]; then
+       echo "Reinstalling $package $newver"
+    elif [ "$cmp" = '>' ]; then
+       echo "Downgrading $package $oldver to $newver"
+    else
+       echo "I'm confused: $*"
+    fi
+}
+
+parse_v1 ()
+{
+    oldpkgs=$(mktemp -t "apt-zephyr-old.XXXXXX") || exit $?
+    newpkgs=$(mktemp -t "apt-zephyr-new.XXXXXX") || exit $?
+    xargs -r -d '\n' dpkg-deb -W | sort -o "$newpkgs"
+    cut -f 1 "$newpkgs" | xargs -r -d '\n' dpkg-query -W | \
+       sort -o "$oldpkgs"
+    join -t '  ' -j 1 -e '-' "$oldpkgs" "$newpkgs" | \
+       while IFS='     ' read -r package oldver newver; do
+       if dpkg --compare-versions "$oldver" lt "$newver"; then
+           package "$package" "$oldver" '<' "$newver"
+       elif dpkg --compare-versions "$oldver" eq "$newver"; then
+           package "$package" "$oldver" '=' "$newver"
+       else
+           package "$package" "$oldver" '>' "$newver"
+       fi
+    done
+    rm -f "$oldpkgs" "$newpkgs"
+}
+
+parse_v2 ()
+{
+    while read -r line && [ -n "$line" ]; do :; done
+    while read -r package oldver cmp newver action; do
+       case "$action" in
+           '**CONFIGURE**' | '**REMOVE**')
+               package "$package" "$oldver" "$cmp" "$newver"
+               ;;
+           '**ERROR**')
+               echo "ERROR on $package $newver"
+               ;;
+       esac
+    done
+}
+
+pre_install_pkgs ()
+{
+    read -r line
+    case "$line" in
+       'VERSION 2')
+           parse_v2 | send_zephyr
+           ;;
+       'VERSION *')
+           echo "$0: unrecognized version: $line" | send-zephyr
+           ;;
+       '')
+           ;;
+       *)
+           (echo "$line"; cat) | parse_v1 | send_zephyr
+           ;;
+    esac
+}
+
+post_invoke ()
+{
+    echo 'Done.' | send_zephyr
+}
+
+if [ "$1" = "--pre-install-pkgs" ]; then
+    pre_install_pkgs
+elif [ "$1" = "--post-invoke" ]; then
+    post_invoke
+else
+    echo "usage: $0 {--pre-install-pkgs | --post-invoke}" >&2
+    echo "(Hint: you probably need to update /etc/apt/apt.conf.d/90zephyr.)" >&2
+fi
+
+exit 0
diff --git a/barn-growl/abstfilter.py b/barn-growl/abstfilter.py
new file mode 100755 (executable)
index 0000000..34755d5
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+##  $Id: abstfilter.py,v 1.1.1.1 2003/07/01 23:28:27 euske Exp $
+##
+##  abstfilter.py - A framework for cascade filters.
+##
+##  from http://www.unixuser.org/~euske/python/index.html:
+##  The following files are in public domain except where otherwise noted. THESE FILES COME WITH ABSOLUTELY NO WARRANTY.
+
+
+##  AbstractFeeder
+##
+class AbstractFeeder(object):
+
+  def __init__(self, next_filter):
+    self.next_filter = next_filter
+    return
+
+  def feed_next(self, s):
+    self.next_filter.feed(s)
+    return
+
+  def close(self):
+    self.next_filter.close()
+    return
+
+
+##  AbstractFilter
+##
+class AbstractFilter(object):
+
+  def __init__(self, next_filter):
+    self.next_filter = next_filter
+    return
+
+  def process(self, s):
+    raise NotImplementedError
+  
+  def feed(self, s):
+    self.feed_next(self.process(s))
+    return
+
+  def feed_next(self, s):
+    self.next_filter.feed(s)
+    return
+
+  def close(self):
+    self.next_filter.close()
+    return
+
+
+##  AbstractConsumer
+##
+class AbstractConsumer(object):
+
+  def __init__(self):
+    return
+
+  def feed(self, s):
+    raise NotImplementedError
+  
+  def close(self):
+    return
+
+
+##  FileGenerator
+##
+class FileGenerator(AbstractFeeder):
+  def __init__(self, next_type):
+    next_filter = next_type(self)
+    AbstractFeeder.__init__(self, next_filter)
+    self.results = []
+    return
+  
+  def feed(self, s):
+    self.results.append(s)
+    return
+
+  def close(self):
+    return
+  
+  def pullopen(self, f):
+    while 1:
+      s = f.readline()
+      if not s: break
+      self.feed_next(s)
+      if self.results:
+        for s in self.results:
+          yield s
+        self.results = []
+    for s in self.results:
+      yield s
+    self.results = []
+    return
+
diff --git a/barn-growl/barn-growl.py b/barn-growl/barn-growl.py
new file mode 100755 (executable)
index 0000000..e36f719
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+
+"""
+Subscribes to zephyr via tzc and sends messages to notification drivers (growl or libnotify).
+"""
+
+import sexpr
+import os
+import subprocess
+import fcntl
+import select
+import sys
+from abstfilter import AbstractConsumer
+import optparse
+import time
+
+class Notifier(AbstractConsumer):
+    def __init__(self, usegrowl, usenotify, useprint):
+        self.usegrowl = usegrowl
+        self.usenotify = usenotify
+        if usenotify:
+            import pynotify
+            pynotify.init("Zephyr")
+            self.pings = {}
+            self.pynotify = pynotify
+        self.useprint = useprint
+        return
+    def feed(self, s):
+        if s is None or type(s) is type(''): return
+        d = dict([(ss[0], len(ss) > 2 and ss[2] or None) for ss in s])
+        if d['tzcspew'] == 'message':
+            zclass = d['class'].lower()
+            zinstance = d['instance'].lower()
+            zop = d['opcode'].lower()
+            zsender = d['sender'].lower()
+            zauth = d['auth'].lower() == 'yes'
+            ztime = "%02d:%02d" % time.strptime(d['time'])[3:5]
+            zmessage = d['message']
+            idtuple = (zclass, zinstance, zsender, ztime)
+            id = '%s/\n%s/\n%s\n %s' % idtuple
+            if zop == 'ping':
+                header = '%s (%s)' % (id, zsender)
+                message = '...'
+            elif zop == 'nil':
+                header = '%s (%s)' % (id, len(zmessage) > 0 and zmessage[0] or zsender)
+                message = '%s' % (len(zmessage) > 1 and zmessage[1] or '')
+            else:
+                return
+            if self.useprint:
+                print (id, header)
+                print message
+            if self.usegrowl:
+                growlnotify = ['growlnotify', '-H', 'localhost', '-a', 'MacZephyr', '-n', 'zephyr', '-d', id, '-t', header]
+                g = subprocess.Popen(growlnotify, stdin=subprocess.PIPE)
+                g.stdin.write(message)
+                g.stdin.close()
+            if self.usenotify:
+                if idtuple in self.pings:
+                    self.pings[idtuple].update(header, message)
+                    self.pings[idtuple].show()
+                else:
+                    n = self.pynotify.Notification(header, message)
+                    n.show()
+                    if zop == 'ping':
+                        self.pings[idtuple] = n
+                self.pings = dict(filter(lambda ((c, i, s, time), v): time == idtuple[3], self.pings.items()))
+    def close(self):
+        return
+
+def main(argv):
+    parser = optparse.OptionParser(usage = '%prog [-s "username@machine"] (--growl | --notify | --print)',
+            description = __doc__.strip())
+    parser.add_option('-s', '--ssh',
+            type = 'string',
+            default = None,
+            dest = 'ssh',
+            help = 'optional remote host to run tzc')
+    parser.add_option('-g', '--growl',
+            action = 'store_true',
+            default = False,
+            dest = 'growl',
+            help = 'use growlnotify for output')
+    parser.add_option('-n', '--notify',
+            action = 'store_true',
+            default = False,
+            dest = 'notify',
+            help = 'use notify-send for output')
+    parser.add_option('-p', '--print',
+            action = 'store_true',
+            default = False,
+            dest = 'useprint',
+            help = 'use stdout for output')
+    opts, args = parser.parse_args()
+
+    usegrowl = opts.growl
+    usenotify = opts.notify
+    useprint = opts.useprint
+    if not usegrowl and not usenotify and not useprint:
+        parser.print_help(sys.stderr)
+        return 1
+    ssh = opts.ssh
+
+    if ssh is None:
+        retval = subprocess.call(['which', 'tzc'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        if retval:
+            print 'tzc not in path.  Please add -s username@machine to specify remote host.'
+            return 1
+
+    if ssh is not None:
+        command = "ssh -o GSSAPIAuthentication=yes -o GSSAPIDelegateCredentials=yes -o GSSAPIKeyExchange=yes %s 'tzc -si'" % ssh
+    else:
+        command = "tzc -si"
+    p = os.popen(command)
+    r = sexpr.SExprReader(Notifier(usegrowl, usenotify, useprint))
+
+    flags = fcntl.fcntl(p, fcntl.F_GETFL)
+    fcntl.fcntl(p, fcntl.F_SETFL, flags | os.O_NONBLOCK)
+
+    try:
+        while 1:
+            [i,o,e] = select.select([p], [], [], 5)
+            if i: s = p.read(1024)
+            else: s = ''
+
+            if s != '':
+                r.feed(s)
+    except KeyboardInterrupt:
+        pass
+    return 0
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
diff --git a/barn-growl/sexpr.py b/barn-growl/sexpr.py
new file mode 100644 (file)
index 0000000..a00f9c5
--- /dev/null
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+##
+##  sexpr.py - by Yusuke Shinyama
+##
+##  * public domain *
+##
+##  from http://www.unixuser.org/~euske/python/index.html:
+##  The following files are in public domain except where otherwise noted. THESE FILES COME WITH ABSOLUTELY NO WARRANTY.
+
+from abstfilter import AbstractFeeder, AbstractFilter, AbstractConsumer
+
+
+##  SExprReader
+##
+class SExprReader(AbstractFilter):
+  """Usage:
+  
+  reader = SExprReader(consumer)
+  reader.feed("(this is (sexpr))")
+  reader.close()
+  """
+  
+  COMMENT_BEGIN = ";"
+  COMMENT_END = "\n"
+  SEPARATOR = " \t\n"
+  PAREN_BEGIN = "("
+  PAREN_END = ")"
+  QUOTE = '"'
+  ESCAPE = "\\"
+
+  def __init__(self, next_filter,
+               comment_begin=COMMENT_BEGIN,
+               comment_end=COMMENT_END,
+               separator=SEPARATOR,
+               paren_begin=PAREN_BEGIN,
+               paren_end=PAREN_END,
+               quote=QUOTE,
+               escape=ESCAPE):
+    AbstractFilter.__init__(self, next_filter)
+    self.comment_begin = comment_begin
+    self.comment_end = comment_end
+    self.separator = separator
+    self.paren_begin = paren_begin
+    self.paren_end = paren_end
+    self.quote = quote
+    self.escape = escape
+    self.special = comment_begin + separator + paren_begin + paren_end + quote + escape
+    self.reset()
+    return
+
+  # SExprReader ignores any error and
+  # try to continue as long as possible.
+  # if you want to throw exception however,
+  # please modify these methods.
+  
+  # called if redundant parantheses are found.
+  def illegal_close_paren(self, i):
+    print "Ignore a close parenthesis: %d" % i
+    return
+  # called if it reaches the end-of-file while the stack is not empty.
+  def premature_eof(self, i, x):
+    print "Premature end of file: %d parens left, partial=%s" % (i, x)
+    return
+
+  # reset the internal states.
+  def reset(self):
+    self.incomment = False              # if within a comment.
+    self.inquote = False                # if within a quote.
+    self.inescape = False               # if within a escape.
+    self.sym = ''                       # partially constructed symbol.
+    # NOTICE: None != nil (an empty list)
+    self.build = None                   # partially constructed list.
+    self.build_stack = []     # to store a chain of partial lists.
+    return self
+
+  # analyze strings
+  def feed(self, tokens):
+    for (i,c) in enumerate(tokens):
+      if self.incomment:
+        # within a comment - skip
+        self.incomment = (c not in self.comment_end)
+      elif self.inescape or (c not in self.special):
+        # add to the current working symbol
+        self.sym += c
+        self.inescape = False
+      elif c in self.escape:
+        # escape
+        self.inescape = True
+      elif self.inquote and (c not in self.quote):
+        self.sym += c
+      else:
+        # special character (blanks, parentheses, or comment)
+        if self.sym:
+          # close the current symbol
+          if self.build == None:
+            self.feed_next(self.sym)
+          else:
+            self.build.append(self.sym)
+          self.sym = ''
+        if c in self.comment_begin:
+          # comment
+          self.incomment = True
+        elif c in self.quote:
+          # quote
+          self.inquote = not self.inquote
+        elif c in self.paren_begin:
+          # beginning a new list.
+          self.build_stack.append(self.build)
+          empty = []
+          if self.build == None:
+            # begin from a scratch.
+            self.build = empty
+          else:
+            # begin from the end of the current list.
+            self.build.append(empty)
+            self.build = empty
+        elif c in self.paren_end:
+          # terminating the current list
+          if self.build == None:
+            # there must be a working list.
+            self.illegal_close_paren(i)
+          else:
+            if len(self.build_stack) == 1:
+              # current working list is the last one in the stack.
+              self.feed_next(self.build)
+            self.build = self.build_stack.pop()
+    return self
+
+  # terminate
+  def terminate(self):
+    # a working list should not exist.
+    if self.build != None:
+      # error - still try to construct a partial structure.
+      if self.sym:
+        self.build.append(self.sym)
+        self.sym = ''
+      if len(self.build_stack) == 1:
+        x = self.build
+      else:
+        x = self.build_stack[1]
+      self.build = None
+      self.build_stack = []
+      self.premature_eof(len(self.build_stack), x)
+    elif self.sym:
+      # flush the current working symbol.
+      self.feed_next(self.sym)
+    self.sym = ''
+    return self
+
+  # closing.
+  def close(self):
+    AbstractFilter.close(self)
+    self.terminate()
+    return
+
+
+##  StrictSExprReader
+##
+class SExprIllegalClosingParenError(ValueError):
+  """It throws an exception with an ill-structured input."""
+  pass
+class SExprPrematureEOFError(ValueError):
+  pass
+class StrictSExprReader(SExprReader):
+  def illegal_close_paren(self, i):
+    raise SExprIllegalClosingParenError(i)
+  def premature_eof(self, i, x):
+    raise SExprPrematureEOFError(i, x)
+  
+
+##  str2sexpr
+##
+class _SExprStrConverter(AbstractConsumer):
+  results = []
+  def feed(self, s):
+    _SExprStrConverter.results.append(s)
+    return
+_str_converter = SExprReader(_SExprStrConverter())
+_str_converter_strict = StrictSExprReader(_SExprStrConverter())
+
+def str2sexpr(s):
+  """parse a string as a sexpr."""
+  _SExprStrConverter.results = []
+  _str_converter.reset().feed(s).terminate()
+  return _SExprStrConverter.results
+def str2sexpr_strict(s):
+  """parse a string as a sexpr."""
+  _SExprStrConverter.results = []
+  _str_converter_strict.reset().feed(s).terminate()
+  return _SExprStrConverter.results
+
+
+##  sexpr2str
+##
+def sexpr2str(e):
+  """convert a sexpr into Lisp-like representation."""
+  if not isinstance(e, list):
+    return e
+  return "("+" ".join(map(sexpr2str, e))+")"
+
+
+# test stuff
+def test():
+  assert str2sexpr("(this ;comment\n is (a test (sentences) (des()) (yo)))") == \
+         [["this", "is", ["a", "test", ["sentences"], ["des", []], ["yo"]]]]
+  assert str2sexpr('''(paren\\(\\)theses_in\\#symbol "space in \nsymbol"
+                   this\\ way\\ also. "escape is \\"better than\\" quote")''') == \
+         [['paren()theses_in#symbol', 'space in \nsymbol', 'this way also.', 'escape is "better than" quote']]
+  str2sexpr("(this (is (a (parial (sentence")
+  return  
+
+
+# main
+if __name__ == "__main__":
+  test()
diff --git a/barnowl/personal-colors.pl b/barnowl/personal-colors.pl
new file mode 100644 (file)
index 0000000..0e40c42
--- /dev/null
@@ -0,0 +1,28 @@
+# Automatically colors personals by hashing the username. Inspired by geofft's
+# .bashrc. Needs a lot of tweaking to make perfect...
+#
+# To use:
+#   :perl do '/mit/snippets/barnowl/personal-colors.pl'
+#   :view -s colors
+
+package BarnOwl::Style::Colors;
+our @ISA=qw(BarnOwl::Style::Default);
+use Digest::MD5;
+
+sub description {"Colors for personals";}
+
+sub format_chat {
+    my $self = shift;
+    my $m = shift;
+    my $body = $self->indent_body($m);
+    if ($m->is_personal) {
+      my @colors = qw{red green blue yellow magenta cyan};
+      my $hash = ord Digest::MD5::md5(($m->direction eq "out") ? $m->pretty_recipient : $m->pretty_sender);
+      $body = '@[@color(' . $colors[$hash % scalar @colors] . ")$body]";
+    }
+    return $self->chat_header($m) . "\n". $body;
+}
+
+BarnOwl::create_style("colors", "BarnOwl::Style::Colors");
+
+1;
diff --git a/barnowl/zcrypt.pl b/barnowl/zcrypt.pl
new file mode 100644 (file)
index 0000000..8ce18ff
--- /dev/null
@@ -0,0 +1,65 @@
+# BarnOwls older than late September 2008 will segfault on short zcrypt messages.
+# Besides, the code is sketchy and doesn't belong in core. This perl module
+# will add :zcrypt and :decrypt commands that use barnowl's zcrypt binary via
+# Perl, so a bug in zcrypt can't possibly affect BarnOwl proper. The :zcrypt
+# command replaces the built-in one.
+#
+# To use this code, type
+#   :perl do '/mit/snippets/barnowl/zcrypt.pl'
+#
+# This first line will disable BarnOwl's own zcrypt code, so messages are
+# encrypted onscreen. Use :decrypt to display them. (Security bugs were later
+# found in the decryption code, although these probably affect locker zcrypt
+# too.)
+
+BarnOwl::command("unset zcrypt");
+
+use IPC::Open2;
+BarnOwl::new_command(decrypt => sub {
+   my $msg = BarnOwl::getcurmsg();
+   my $cmd = shift;
+   my @args = @_;
+   if (scalar @args == 0) {
+       @args = ('-c', $msg->class, '-i', $msg->instance);
+   }
+   my ($zo, $zi);
+   my $pid = open2($zo, $zi, 'athrun', 'barnowl', 'zcrypt', '-D', @args) or die "Couldn't launch zcrypt\n";
+   my $decrypted;
+   print $zi @{$msg->fields}[1] . "\n";
+   close $zi;
+   while (<$zo>) {
+      chomp;
+      last if $_ eq "**END**";
+      $decrypted .= "$_\n";
+   }
+   BarnOwl::popless_ztext($decrypted);
+   waitpid $pid, 0;
+   },
+   {summary => "Decrypt a zcrypted message once",
+    usage => "decrypt [args]",
+    description => "Invokes athrun barnowl zcrypt on the current message,\n
+using the class and instance to find the crypt key, and pops up the\n
+decrypted output. If args are specified, they are passed to zcrypt and the\n
+class and instance are ignored.\n\n
+SEE ALSO: zcrypt(1)"});
+
+BarnOwl::new_command(zcrypt => sub {
+   my $cmd = shift;
+   my @args = @_;
+   #my $argstring = BarnOwl::quote(@args);  # requires BarnOwl 1.4
+   my $argstring = join ' ', map { BarnOwl::quote($_) } @args;
+   BarnOwl::start_edit_win("athrun barnowl zcrypt $argstring", sub {
+      my $msg = shift;
+      my ($zo, $zi);
+      my $pid = open2($zo, $zi, 'athrun', 'barnowl', 'zcrypt', @args);
+      print $zi "$msg\n";
+      close $zi;
+      local $/;
+      BarnOwl::message(<$zo>);
+      waitpid $pid, 0;
+      });
+   },
+   {summary => "Run athrun barnowl zcrypt",
+    usage => "zcrypt -c [class] -i [instance]",
+    description => "Calls athrun barnowl zcrypt on a message you type in.\n\n
+SEE ALSO: zcrypt(1)"});
diff --git a/bash/newline-fix.bash b/bash/newline-fix.bash
new file mode 100644 (file)
index 0000000..8295e87
--- /dev/null
@@ -0,0 +1,53 @@
+# newline-fix.bash: Force the bash prompt to start after a newline
+# http://snippets.scripts.mit.edu/gitweb.cgi/.git/blob/HEAD:/bash/newline-fix.bash
+#
+# Some commands don’t print a newline at the end of their output, or
+# take long enough to return that you’ve already typed part of the
+# next command.  Either causes bash to start its prompt in the middle
+# of a line, which confuses it.  This script sets $PROMPT_COMMAND to
+# echo a magic sequence of terminal commands that will display a red
+# “<no LF>\n” before the prompt if it would otherwise start in the
+# middle of a line.
+#
+# Copyright © 2010 Anders Kaseorg <andersk@mit.edu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# Usage: Source this file from your ~/.bashrc.
+
+show_no_lf () {
+    tput -S <<NOLF && echo -n '<no LF>' && \
+    tput -S <<SPSP && echo -n '  ' && \
+    tput -S <<EOF
+sc
+tbc
+cr
+cuf 8
+hts
+rc
+setaf 1
+rev
+NOLF
+sgr0
+ht
+sc
+tbc
+cr
+$(for ((i=8; i<=320; i+=8)); do echo cuf 8; echo hts; done)
+rc
+SPSP
+cr
+el
+EOF
+}
+
+show_no_lf="$(show_no_lf)" && \
+PROMPT_COMMAND="echo -n \"\$show_no_lf\" >&2;$PROMPT_COMMAND"
diff --git a/certs/pkcs2pem b/certs/pkcs2pem
new file mode 100755 (executable)
index 0000000..513b32e
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+set -e
+
+usage() {
+    cat <<EOF
+Usage: $0 <cert.p12> <cert.pem>
+
+Transforms a .p12 file, for instance as exported by Firefox's
+cerfiticate "backup" feature, into a PEM file that contains your
+private key and certificate.
+
+To export your certificate from Firefox, go to Edit|Preferences,
+Advanced|Security|View Certificates, and ``Backup'' your certificate
+to a file. Firefox will save it as a PKCS12 certificate. You must
+enter a passphrase, which this script will prompt you for.
+
+EOF
+    exit 1
+}
+
+[ "$#" -eq 2 ] || usage
+
+pkcs="$1"
+pem="$2"
+
+openssl pkcs12 -in "$pkcs" -nodes -out "$pem"
+
+cat >&2 <<EOF
+Private key and certificate written to $pem
+
+Keep this file safe!
+
+You can pass this to wget's --certificate and --private-key options,
+or to curl's --cert option.
+
+To use it with perl's LWP, set the following environment variables:
+
+EOF
+
+pem=$(readlink -f "$pem")
+
+printf 'HTTPS_CERT_FILE=%q\n' "$pem"
+printf 'HTTPS_KEY_FILE=%q\n' "$pem"
+
diff --git a/crondiff/crondiff.pl b/crondiff/crondiff.pl
new file mode 100644 (file)
index 0000000..b30cb46
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+
+=head1 NAME
+
+crondiff.pl - Run command periodically and compare their output.
+
+=head1 DESCRIPTION
+
+crondiff.pl is designed to be run from cron periodically. It takes as
+input a list of commands to run. Each time it is invoked, it runs the
+commands listed, and sends mail to the user running the script if the
+output of that command has changed since the last run.
+
+=head1 USAGE
+
+=over 4
+
+=item
+
+Create a file C<~/.crondiff/cmdlist>, containing one line per file, of
+commands to be checked.
+
+=item
+
+Add C<crondiff.pl> to your crontab. The following example runs it
+directly out of the locker (not necessarily a good idea!) every hour:
+
+    # m h  dom mon dow   command
+      0 *  *   *   * PATH=/bin:/usr/bin: perl /mit/snippets/crondiff/crondiff.pl
+
+=back
+
+=head1 ASSUMPTIONS
+
+=over 4
+
+=item
+
+Mail goes to C<$USER@mit.edu>, for the user who runs this script.
+
+=item
+
+Mail will be sent using C<mutt -x>.
+
+=back
+
+If you don't like these assumptions, patches are welcome :)
+
+=cut
+
+my $confdir = $ENV{HOME} . "/.crondiff";
+unless($ENV{USER}) {
+    chomp(my $user = `id -un`);
+    $ENV{USER} = $user;
+};
+my $mailto = $ENV{USER} . '@mit.edu';
+my @mailer = qw(mutt -x);
+
+my $cachedir = "$confdir/cache";
+
+mkdir("$cachedir") unless -e "$cachedir";
+
+open(my $clist, "<", "$confdir/cmdlist")
+  or exit;
+
+while(my $cmd = <$clist>) {
+    chomp($cmd);
+    my $hash = md5_hex($cmd);
+    my $pid = fork;
+    if($pid == 0) {
+        # Child
+        close(STDOUT);
+        close(STDERR);
+        open(STDOUT, ">>", "$cachedir/$hash.new");
+        open(STDERR, ">>", "$cachedir/$hash.new");
+        exec($cmd);
+    }
+    waitpid($pid, 0);
+    if($? != 0) {
+        # Exited with error, should send mail, but punting for now.
+        unlink("$cachedir/$hash.new");
+        next;
+    } elsif(-f "$cachedir/$hash"
+       && system("diff -q $cachedir/$hash.new $cachedir/$hash > /dev/null 2>&1") != 0) {
+        # Output changed
+        if(($pid = fork) == 0) {
+            close(STDOUT);
+            open(STDOUT, "|-", @mailer, $mailto, '-s', "Output of [$cmd]");
+            system("date");
+            print "----\n";
+            system("diff", "-u", "-U", "0", "$cachedir/$hash", "$cachedir/$hash.new");
+            exit;
+        } else {
+            waitpid($pid, 0);
+        }
+    }
+    rename("$cachedir/$hash.new", "$cachedir/$hash");
+}
diff --git a/git-hooks/repo-noroot-update b/git-hooks/repo-noroot-update
new file mode 100755 (executable)
index 0000000..66b2efe
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# A hook script that blocks committer and author names that are
+# root (or a configured blacklisted name).
+# XXX: should allow only applying this to certain branches.
+#
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# Config
+# ------
+# hooks.noroot.branches
+#   List of branches for which commits by root should be disallowed.
+# hooks.noroot.match
+#   XXX: figure out format
+#
+
+set -e
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+       echo "Don't run this script from the command line." >&2
+       echo " (if you want, you could supply GIT_DIR then run" >&2
+       echo "  $0 <ref> <oldrev> <newrev>)" >&2
+       exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+       echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
+       exit 1
+fi
+
+# --- Config
+branches=$(git config --get hooks.noroot.branches || echo "")
+# XXX: hooks.noroot.match
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
+       newrev_type=delete
+else
+       newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+       refs/heads/*,commit)
+               git log --pretty="format:%h \"%an\" \"%cn\"%n" "$oldrev".."$newrev" | \
+               while read hash an cn; do
+                       if [ "$an" = "\"root\"" -o "$cn" = "\"root\"" ]; then
+                               echo "*** Committing as root not allowed in this repository," >&2
+                               echo "*** Please fix your GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL,"
+                               echo "*** GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL."
+                               echo "*** Offending commit was $hash."
+                               exit 1
+                       fi
+               done
+               ;;
+       refs/remotes/*,commit)
+               # tracking branch
+               ;;
+       refs/tags/*,*)
+               # we could track tags, but we've decided they're not
+               # interesting
+               ;;
+       *,delete)
+               # not interesting
+               ;;
+       *)
+               # Anything else (is there anything else?)
+               echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+               exit 1
+               ;;
+esac
+
+# --- Finished
+exit 0
diff --git a/git-hooks/zephyr-post-receive b/git-hooks/zephyr-post-receive
new file mode 100755 (executable)
index 0000000..1a21496
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# This script is run after receive-pack has accepted a pack and the
+# repository has been updated.  It is passed arguments in through stdin
+# in the form
+#  <oldrev> <newrev> <refname>
+# For example:
+#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
+
+class=`git config zephyr.class`
+instance=`git config zephyr.instance`
+zsig=`git config zephyr.zsig`
+color=`git config --bool zephyr.color`
+
+if [ "${color:-true}" = "true" ]; then
+    usecolor="--color"
+else
+    usecolor=""
+fi
+
+if [ -z "$zsig" ]; then
+    if [ -e "$GIT_DIR/description" ]; then
+        zsig=`cat "$GIT_DIR/description"`
+    fi
+    if [ -z "$zsig" ] || \
+        [ "$zsig" = "Unnamed repository; edit this file to name it for gitweb." ] || \
+        [ "$zsig" = "Unnamed repository; edit this file 'description' to name the repository." ]; then
+        zsig=$(basename "$(cd "$GIT_DIR" && pwd)")
+        if [ "$zsig" = ".git" ]; then
+            zsig=$(basename "$(cd "$GIT_DIR/.." && pwd)")
+        fi
+    fi
+fi
+
+if [ -z "$class" ]; then
+  echo "I don't know where to send a commit zephyr!" >&2
+  echo "Please set the zephyr.class config variable in" >&2
+  echo "$PWD/config." >&2
+  exit 1
+fi
+while read oldrev newrev refname; do
+  if [ "$oldrev" = "0000000000000000000000000000000000000000" ]; then
+    # dammit git
+    zwrite -c "$class" -i "${instance:-${refname#refs/heads/}}" -s "$zsig: $refname" -d \
+      -m "New branch $refname created, currently at $newrev."
+    continue
+  fi
+  git rev-list --first-parent --reverse "$oldrev..$newrev" | while read rev; do
+    shortrev=`git log -1 --pretty=format:%h "$rev"`
+    (git show --stat -M $usecolor "$rev" |
+     sed -e 's/@/@@/g' \
+         -e 's/}/@(})/g' \
+         -e 's/\e\[m/}@{/g' \
+         -e 's/\e\[33m/@color(yellow)/g' \
+         -e 's/\e\[31m/@color(red)/g' \
+         -e 's/\e\[32m/@color(green)/g' \
+         -e '1s/^/@{/' \
+         -e '$s/$/}/') |
+    zwrite -c "$class" -i "${instance:-$shortrev}" -s "$zsig: $refname" -d
+  done
+done
diff --git a/git-rcsimport b/git-rcsimport
new file mode 100755 (executable)
index 0000000..4b06a00
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+# This is a trivial wrapper around git-cvsimport.
+
+dir="$1"
+shift
+if [ -z "$dir" ]; then
+  echo "Usage: git-rcsimport path/to/dir"
+  exit 1
+fi
+if [ ! -d "$dir/RCS" ]; then
+  echo "$dir/RCS does not exist."
+  exit 1
+fi
+
+tmpdir=`mktemp -d -t git-rcsimport-XXXXXXXXXX`
+mkdir "$tmpdir/CVSROOT" "$tmpdir/locks" "$tmpdir/rcs"
+echo "LockDir=$tmpdir/locks" > "$tmpdir/CVSROOT/config"
+absdir=`cd "$dir"; pwd`
+(cd "$absdir"; find . -name RCS -type d) | while read dir; do
+    rcsdir=$tmpdir/rcs/`dirname "$dir"`
+    mkdir -p "$rcsdir"
+    ln -s "$absdir/$dir"/* "$rcsdir"
+done
+CVSROOT="$tmpdir" git cvsimport "$@" rcs
+rm -r "$tmpdir"
diff --git a/iptables-nat.sh b/iptables-nat.sh
new file mode 100644 (file)
index 0000000..eb9e5f8
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# usage (e.g.): sudo sh iptables-nat.sh [public interface] [private interface]
+#
+# Sets up iptables on Linux 2.6 to run a NAT for all machines on the
+# private interface via the IP bound to the public interface. You should
+# manually configure a private RFC1918 network for the machines on the
+# private interface.
+#
+# Derived from the Linux IP masquerade HOWTO.
+
+public=${1:-ath0}
+private=${2:-eth0}
+
+# permit forwarding connections that have to do with the NAT
+iptables -A FORWARD -i $private -o $public -j ACCEPT
+iptables -A FORWARD -i $public -o $private -m state --state ESTABLISHED,RELATED -j ACCEPT
+
+# drop other connections, else the remaining commands will NAT public requests
+# routed to this machine
+iptables -P FORWARD DROP
+
+# enable NAT
+iptables -t nat -A POSTROUTING -o $public -j MASQUERADE
+
+# this is usually not on by default
+echo 1 > /proc/sys/net/ipv4/ip_forward
diff --git a/kerberos/kdo b/kerberos/kdo
new file mode 100644 (file)
index 0000000..e06910f
--- /dev/null
@@ -0,0 +1,118 @@
+# -*- mode: sh -*-
+# kdo is a shell function for interacting with multiple Kerberos
+# credential caches.
+#
+# To use kdo, add this snippet to your .bashrc or .bashrc.mine file.
+#
+# To run a command with a different set of credentials from your
+# default, run
+#
+#     kdo <principal> <command>
+#
+# e.g.,
+#
+#     kdo broder/root aklog
+#
+# If you lack credentials for the specified principal, you'll be
+# prompted for the password.
+#
+# If kdo needs to acquire tickets, it will pass the value of
+# ${kdo_args[@]} to kinit. I use this to get tickets that last for 15
+# minutes, that are renewable for 60 minutes, and aren't forwardable.
+#
+# To add kdo support for a new platform, you need to provide an
+# interface to multiple credential caches by defining two functions:
+#
+#  - kcaches::
+#      Print one line per current credential cache of the form "<PRINCIPAL> <KRB5CCNAME>"
+#  - knewcache::
+#      Without changing the current credentials cache, get credentials
+#      for the principal in $1, passing the remaining arguments to
+#      kinit.
+#      knewcache should set the variable cache with the KRB5CCNAME
+#      value for the newly created credential cache
+#
+# Also included is krootssh, a wrapper around ssh for using your
+# root-instance tickets with ssh. It ensures that your tickets don't
+# get accidentally forwarded, on the off chance that you have
+# forwardable tickets.
+
+# CONFIGURATION
+kdo_args=(-l15m -r60m -F)
+
+# CC interface for OS X
+if [ "Darwin" = "$(uname)" ]; then
+    kcaches () {
+        klist -A | perl -ne '$cache = $1 if /^Kerberos 5 ticket cache: '\''(.*)'\''/; print "$1 $cache\n" if /^Default principal: (.*)$/'
+    }
+
+    knewcache () {
+        princ="$1"; shift
+        local oldcache="$(klist | grep 'Kerberos 5 ticket cache' | cut -f 2 -d "'")"
+        # " # <-- emacs thinks there's an unbalanced " on the previous line.
+        kinit "$@" "$princ" || return 1
+        cache="$(kfindcache "$princ")"
+        # On OS X, kinit will switch your default credential cache to
+        # that of the newly acquired tickets, so switch back if we can
+        if [ -z "$oldcache" ]; then
+            echo "W: Tickets for $princ are now in your default credential cache" >&2
+        else
+            kswitch -c "$oldcache"
+        fi
+    }
+fi
+
+# If kcaches and knewcache have been defined for this platform, then
+# setup kdo. Otherwise, add a helpful error.
+if hash kcaches &>/dev/null && hash knewcache &>/dev/null; then
+    kfindcache () {
+        kcaches | fgrep "$1" | cut -d' ' -f2-
+    }
+
+    kdo () {
+        local princ="$1"; shift
+        local cache="$(kfindcache "$princ")"
+        # If the cache that we want to use has expired tickets, then
+        # destroy that cache so we don't try to use it again and clear
+        # $cache so that we'll revert to acquiring a new set of
+        # tickets
+        if [ -n "$cache" ] && ! klist -s "$cache"; then
+            KRB5CCNAME="$cache" kdestroy
+            cache=""
+        fi
+        if [ -z "$cache" ]; then
+            knewcache "$princ" "${kdo_args[@]}" || return 1
+        fi
+        echo "I: Running $1 with cache $cache (for principal $princ)" >&2
+        KRB5CCNAME="$cache" "$@"
+    }
+    _kdo () {
+        local cur
+        COMPREPLY=()
+        cur="${COMP_WORDS[COMP_CWORD]}"
+        opts="$(kcaches | awk '{ print $1 }')"
+        case $COMP_CWORD in
+            1)
+                COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))
+                ;;
+            2)
+                COMPREPLY=($(compgen -c -- "${cur}"))
+        esac
+    }
+    complete -o bashdefault -F _kdo kdo
+
+    krootssh () {
+        kdo ${ATHENA_USER:-$USER}/root@ATHENA.MIT.EDU ssh -o GSSAPIDelegateCredentials=no "$@"
+    }
+else
+    kdo () {
+        echo "kdo has not been ported to this platform yet." >&2
+        return 1
+    }
+
+    krootssh () {
+        echo "kdo has not been ported to this plastform yet." >&2
+        return 1
+    }
+fi
+
diff --git a/kerberos/krbroot b/kerberos/krbroot
new file mode 100755 (executable)
index 0000000..5078d15
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+export KRB5CCNAME=/tmp/krb5cc_$(id -u).root
+export KRBTKFILE=/tmp/tkt$(id -u).root
+
+case $1 in
+    init)
+        shift;
+        exec kinit -F -5 -l15m -r15m $USER/root@ATHENA.MIT.EDU "$@"
+        ;;
+    destroy)
+        exec kdestroy -45
+        ;;
+    shell)
+        klist -s || krbroot init || exit 1;
+        HOST="`hostname` (krbroot)" pagsh -c $SHELL
+        ;;
+    ssh)
+        klist -s || krbroot init || exit 1;
+        shift
+        exec ssh -k -l root "$@"
+        ;;
+    rlogin)
+        klist -s || krbroot init || exit 1;
+        exec rlogin -x -l root $2
+        ;;
+    *)
+        if [ $# = 0 ]; then
+            echo "Usage: $0 init" >&2
+            echo "       $0 destroy" >&2
+            echo "       $0 shell" >&2
+            echo "       $0 ssh [args]" >&2
+            echo "       $0 rlogin [args]" >&2
+            echo "       $0 [cmd]" >&2
+        else
+            exec "$@"
+        fi
+        ;;
+esac
diff --git a/programming/disasm b/programming/disasm
new file mode 100755 (executable)
index 0000000..0531dc5
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+file=$(mktemp)
+echo "$*" | xxd -r -p > "$file"
+objdump -D -b binary -m i386 "$file" | tail -n +7
+rm "$file"
diff --git a/programming/gccrun b/programming/gccrun
new file mode 100755 (executable)
index 0000000..55e6b68
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+if [ "$#" = 0 ]; then
+    echo "usage: $0 [-w wrapper] <C code...>" >&2
+    exit 1
+fi
+
+if [ "$1" = -w ]; then
+    wrapper="$2"
+    shift 2
+fi
+
+f=$(mktemp -d -t gccrun.XXXXXXXX) || exit 1
+cat > "$f/command.c" << EOF
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(int argc, char *argv[], char *envp[])
+{
+       $@;
+       return 0;
+}
+EOF
+if ! gcc -o "$f/command" "$f/command.c"; then
+    exit 1
+fi
+if [ -n "$wrapper" ]; then
+    "$wrapper" "$f/command"
+else
+    "$f/command"
+fi
+r=$?
+rm -r "$f"
+exit $r
diff --git a/rt/zephyr-scrip.pl b/rt/zephyr-scrip.pl
new file mode 100644 (file)
index 0000000..15f797b
--- /dev/null
@@ -0,0 +1,53 @@
+my $class = 'scripts-test';
+my $instance_prefix = 'rt.';
+my @zwrite = ('/usr/athena/bin/zwrite', '-d', '-O', 'auto', '-c', $class);
+
+# RT-to-Zephyr notification scrip
+# http://snippets.scripts.mit.edu/gitweb.cgi/.git/blob/HEAD:/rt/zephyr-scrip.pl
+#
+# Copyright © 2010 Anders Kaseorg <andersk@mit.edu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# Usage: configure $class and $instance_prefix above, and create a
+# scrip as follows.
+#
+#   Description: Send Zephyr
+#   Condition: User Defined
+#   Action: User Defined
+#   Template: Global template: Blank
+#   Stage: TrasnactionCreate
+#   Custom condition:
+#     1;
+#   Custom action preparation code:
+#     1;
+#   Custom action cleanup code:
+#     [insert this code]
+
+sub send_notice {
+    my ($instance, $body, $extra) = @_;
+    open my $out, '|-', @zwrite, '-i', $instance, defined $extra ? ('-s', $extra) : ();
+    print $out $body;
+    close $out;
+};
+
+local $SIG{__DIE__} = sub {
+    my ($err) = @_;
+    $err =~ s/@/@@/g;
+    send_notice "${instance_prefix}error", "Internal error in Zephyr scrip:\n$err";
+};
+
+(my $id = $self->TransactionObj->Ticket) =~ s/@/@@/g;
+(my $description = $self->TransactionObj->Description) =~ s/@/@@/g;
+(my $subject = $self->TransactionObj->TicketObj->Subject) =~ s/@/@@/g;
+
+send_notice "$instance_prefix$id", $description, $subject;
+1;
diff --git a/svn-hooks/commit-zephyr b/svn-hooks/commit-zephyr
new file mode 100755 (executable)
index 0000000..c0b48d7
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# This is a script that can be called from a Subversion post-commit hook
+# to zephyr a summary of the commit or the full commit.
+#
+# Use by putting something like the following in hooks/post-commit:
+# REPOS="$1"
+# REV="$2"
+# /mit/snippets/svn-hooks/commit-zephyr "$REPOS" "$REV" -c scripts
+# /mit/snippets/svn-hooks/commit-zephyr "$REPOS" "$REV" --full -c scripts-auto -i commits
+
+export LC_ALL=en_US.UTF-8
+
+CLASS=test
+INSTANCE=@
+FULL=0
+
+OPTS=$(getopt -o c:i:f -l class:,instance:,full -n "$0" -- "$@") || exit $?
+eval set -- "$OPTS"
+while :; do
+    case "$1" in
+       -c|--class) CLASS=$2; shift 2;;
+       -i|--instance) INSTANCE=$2; shift 2;;
+       -f|--full) FULL=1; shift;;
+       --) shift; break;;
+       *) exit 1;;
+    esac
+done
+[ $# -ge 2 ] || exit 1
+REPOS=$1
+REV=$2
+
+if [ "$INSTANCE" = "${INSTANCE%@}@" ]; then
+    INSTANCE=${INSTANCE%@}r$REV
+fi
+
+dirs=$(svnlook dirs-changed "$REPOS" -r "$REV")
+svnlook info "$REPOS" -r "$REV" | (
+    read -r author
+    read -r datestamp
+    read -r logsize
+    log=$(cat)
+    echo "r$REV by $author $datestamp"
+    echo "$log"
+    svnlook changed "$REPOS" -r "$REV"
+    if [ "$FULL" -eq 1 ]; then
+       echo
+       svnlook diff "$REPOS" -r "$REV"
+    else
+       echo svnlook diff "$REPOS" -r "$REV"
+    fi
+) | zwrite -d -c "$CLASS" -i "$INSTANCE" -O "auto" -s "SVN: r$REV"
diff --git a/svn-hooks/zephyr-post-revprop-change b/svn-hooks/zephyr-post-revprop-change
new file mode 100755 (executable)
index 0000000..af15922
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+export LC_ALL=en_US.UTF-8
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+OLD_VAL="$(cat)"
+NEW_VAL="$(svnlook propget --revprop -r "$REV" "$REPOS" "$PROPNAME")"
+#export PATH="/usr/bin:/bin"
+
+(echo "$USER @(@color(red)changing revprop) @b($PROPNAME)"; echo "from: $OLD_VAL"; echo "to: $NEW_VAL") | zwrite -d -c test -i "r$REV"