Remove a blog post and add some source code
This commit is contained in:
parent
5d2dbe6b9c
commit
6840df22b6
24
src.mdwn
24
src.mdwn
|
@ -4,28 +4,6 @@ I write software for a living. I like doing it so much that sometimes I
|
||||||
even write software just for fun. Here are some of the better hacks I've
|
even write software just for fun. Here are some of the better hacks I've
|
||||||
created over the years.
|
created over the years.
|
||||||
|
|
||||||
[Firebot](firebot) is an IRC automaton (bot) that works as an InfoBot,
|
[[!map pages="src/*" show="title"]]
|
||||||
LinkBot (links multiple channels, even across servers), command bot
|
|
||||||
(displays the output of programs), web bot (fetches and parses web
|
|
||||||
pages, returning the results), and more. Everything runs in an
|
|
||||||
asynchronous select() loop with a very easy-to-use high-level interface.
|
|
||||||
|
|
||||||
[Photobob](photobob) is a web-based photo album. There's nothing to
|
|
||||||
differentiate this from the myriad other web-based photo albums out
|
|
||||||
there, except that this one doesn't need a database and doesn't write
|
|
||||||
any meta-information. You just point it at a directory full of
|
|
||||||
images/movies and it serves 'em up.
|
|
||||||
|
|
||||||
[eguile](eguile) is a text preprocessor using guile scheme.
|
|
||||||
[escm](escm) is the same thing for gauche. You could use these to build
|
|
||||||
a web site with a common theme. I used to use them, but now I use a
|
|
||||||
very simple m4 template.
|
|
||||||
|
|
||||||
The [Universal Boardgame Kit](ubk/) is a kit you can build for under $5
|
|
||||||
to use with a number of boardgames. A few boards are included. I
|
|
||||||
designed the pieces and most of the boards, and hand-coded them all in
|
|
||||||
PostScript. That makes me feel manly.
|
|
||||||
|
|
||||||
I also have some little [Python](python/), [PostScript](ps-hacks), and
|
|
||||||
[other](misc) hacks, which do clever things.
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[[!meta title="Firebot"]]
|
[[!meta title="Firebot: IRC Automaton"]]
|
||||||
|
|
||||||
FireBot is a winner!
|
FireBot is a winner!
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ of a firewall. You can use it to snoop on traffic, modify or discard
|
||||||
certain packets, make routing decisions, masquerade stuff, whatever--and
|
certain packets, make routing decisions, masquerade stuff, whatever--and
|
||||||
you get it all with garbage collection :)
|
you get it all with garbage collection :)
|
||||||
|
|
||||||
|
Apparently this program appears in a book called "Security Power Tools".
|
||||||
|
That means I'm *Internet Famous*!
|
||||||
|
|
||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -22,6 +25,12 @@ License
|
||||||
|
|
||||||
GPL, of course.
|
GPL, of course.
|
||||||
|
|
||||||
|
Support
|
||||||
|
-------
|
||||||
|
|
||||||
|
You can email me at <neale@woozle.org>, or try the IRC channel
|
||||||
|
[#py-ipqueue on irc.oftc.net](irc://irc.oftc.net/py-ipqueue).
|
||||||
|
|
||||||
Screen Shots
|
Screen Shots
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
ipqueue-1.4.1.tar.gz
|
|
Binary file not shown.
|
@ -0,0 +1,265 @@
|
||||||
|
#! /usr/bin/gosh
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Neale Pickett <neale@woozle.org> wrote this.
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; This uses gauche's networking stuff, but no other gauche stuff. It
|
||||||
|
;; should be simple to substitute your implementation's networking
|
||||||
|
;; procedures.
|
||||||
|
(use gauche.net)
|
||||||
|
(require-extension (srfi 1 4 8 9))
|
||||||
|
|
||||||
|
(define message-specs
|
||||||
|
;; name num format
|
||||||
|
'((TVersion 100 (2 4 s)) ;x64
|
||||||
|
(RVersion 101 (2 4 s))
|
||||||
|
(TAuth 102 (2 4 s s))
|
||||||
|
(RAuth 103 (2 13))
|
||||||
|
(TAttach 104 (2 4 4 s s)) ;x68
|
||||||
|
(RAttach 105 (2 13))
|
||||||
|
(TError 106 ()) ;illegal
|
||||||
|
(RError 107 (2 s))
|
||||||
|
(TFlush 108 (2 2)) ;x6c
|
||||||
|
(RFlush 109 (2))
|
||||||
|
(TWalk 110 (2 4 4 (2 . s)))
|
||||||
|
(RWalk 111 (2 (2 . 13)))
|
||||||
|
(TOpen 112 (2 4 1)) ;x70
|
||||||
|
(ROpen 113 (2 13 4))
|
||||||
|
(TCreate 114 (2 4 s 4 1))
|
||||||
|
(RCreate 115 (2 13 4))
|
||||||
|
(TRead 116 (2 4 8 4)) ;x74
|
||||||
|
(RRead 117 (2 (4 . d)))
|
||||||
|
(TWrite 118 (2 4 8 (4 . s)))
|
||||||
|
(RRwrite 119 (2 4))
|
||||||
|
(TClunk 120 (2 4)) ;x78
|
||||||
|
(RClunk 121 (2))
|
||||||
|
(TRemove 122 (2 4))
|
||||||
|
(RRemove 123 (2))
|
||||||
|
(TStat 124 (2 4)) ;x7c
|
||||||
|
(RStat 125 (2 n))
|
||||||
|
(TWStat 126 (2 4 n))
|
||||||
|
(RWStat 127 (2)))) ;x7f
|
||||||
|
|
||||||
|
(define (spec-by-num num)
|
||||||
|
(let loop ((specs message-specs))
|
||||||
|
(cond
|
||||||
|
((null? specs)
|
||||||
|
#f)
|
||||||
|
((equal? (cadar specs) num)
|
||||||
|
(car specs))
|
||||||
|
(else
|
||||||
|
(loop (cdr specs))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Helper procedures
|
||||||
|
;;
|
||||||
|
|
||||||
|
(define (u8-list->uint l)
|
||||||
|
(let loop ((l (reverse l))
|
||||||
|
(acc 0))
|
||||||
|
(if (null? l)
|
||||||
|
acc
|
||||||
|
(loop (cdr l)
|
||||||
|
(+ (* 256 acc)
|
||||||
|
(car l))))))
|
||||||
|
|
||||||
|
(define (uint->u8-list width i)
|
||||||
|
(if (zero? width)
|
||||||
|
'()
|
||||||
|
(let ((b (modulo i 256))
|
||||||
|
(r (floor (/ i 256))))
|
||||||
|
(cons b (uint->u8-list (- width 1) r)))))
|
||||||
|
|
||||||
|
;; XXX: s had better be printable 7-bit ASCII
|
||||||
|
(define (string->u8-list s)
|
||||||
|
(map char->integer (string->list s)))
|
||||||
|
|
||||||
|
(define (u8-list->string l)
|
||||||
|
(list->string (map integer->char l)))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Packing and unpacking, both deal with u8-lists
|
||||||
|
;;
|
||||||
|
|
||||||
|
(define (pack fmt args)
|
||||||
|
(let loop ((fmt fmt)
|
||||||
|
(args args)
|
||||||
|
(acc '()))
|
||||||
|
;;(write (list fmt args acc)) (newline)
|
||||||
|
(cond
|
||||||
|
((null? fmt)
|
||||||
|
acc)
|
||||||
|
((number? (car fmt))
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(cdr args)
|
||||||
|
(append acc (uint->u8-list (car fmt) (car args)))))
|
||||||
|
((equal? (car fmt) 's)
|
||||||
|
;;XXX Should handle UTF-8
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(cdr args)
|
||||||
|
(append acc
|
||||||
|
(uint->u8-list 2 (string-length (car args)))
|
||||||
|
(string->u8-list (car args)))))
|
||||||
|
((pair? (car fmt))
|
||||||
|
;; fmt item is (c . type), which gets packed to a c-octet n,
|
||||||
|
;; followed by n types.
|
||||||
|
(let ((count (length (car args))))
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(cdr args)
|
||||||
|
(append acc
|
||||||
|
(uint->u8-list (caar fmt) count)
|
||||||
|
(pack (make-list count (cdar fmt))
|
||||||
|
(car args))))))
|
||||||
|
((equal? (car fmt) 'n)
|
||||||
|
;; XXX: total guess here
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(cdr args)
|
||||||
|
(append acc (car args))))
|
||||||
|
(else
|
||||||
|
(error (format "Unknown format element: ~a" (car fmt)))))))
|
||||||
|
|
||||||
|
(define (unpack fmt l)
|
||||||
|
(reverse
|
||||||
|
(let loop ((fmt fmt)
|
||||||
|
(l l)
|
||||||
|
(acc '()))
|
||||||
|
;;(write (list fmt l acc)) (newline)
|
||||||
|
(cond
|
||||||
|
((null? fmt)
|
||||||
|
acc)
|
||||||
|
((number? (car fmt))
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(drop l (car fmt))
|
||||||
|
(cons (u8-list->uint (take l (car fmt)))
|
||||||
|
acc)))
|
||||||
|
((equal? (car fmt) 's)
|
||||||
|
(let ((len (u8-list->uint (take l 2)))
|
||||||
|
(m (drop l 2)))
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(drop m len)
|
||||||
|
(cons (u8-list->string (take m len))
|
||||||
|
acc))))
|
||||||
|
((pair? (car fmt))
|
||||||
|
(let* ((count (u8-list->uint (take l (caar fmt))))
|
||||||
|
(m (drop l (caar fmt))))
|
||||||
|
(receive (p octets)
|
||||||
|
(case (cdar fmt)
|
||||||
|
((s)
|
||||||
|
(let ((p (reverse (unpack (make-list count (cdar fmt))
|
||||||
|
l))))
|
||||||
|
(values p
|
||||||
|
(reduce + 0 (map string-length p)))))
|
||||||
|
((d)
|
||||||
|
(values (take m count)
|
||||||
|
count))
|
||||||
|
(else
|
||||||
|
(values (reverse (unpack (make-list count (cdar fmt))
|
||||||
|
l))
|
||||||
|
(* count (cdar fmt)))))
|
||||||
|
(loop (cdr fmt)
|
||||||
|
(drop m octets)
|
||||||
|
(cons p acc)))))
|
||||||
|
|
||||||
|
(else
|
||||||
|
(error (format "Unknown format element: ~a" (car fmt))))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Packet assembly and disassembly
|
||||||
|
;;
|
||||||
|
|
||||||
|
(define (make-packet type . args)
|
||||||
|
(let* ((spec (cdr (assoc type message-specs)))
|
||||||
|
(msgnum (car spec))
|
||||||
|
(fmt (cadr spec))
|
||||||
|
(p (pack fmt args)))
|
||||||
|
(append (uint->u8-list 4 (+ 5 (length p)))
|
||||||
|
(list msgnum)
|
||||||
|
p)))
|
||||||
|
|
||||||
|
(define (write-packet ixp type . args)
|
||||||
|
((ixp-write ixp) (apply make-packet (cons type args))))
|
||||||
|
|
||||||
|
(define (read-uint width ixp)
|
||||||
|
(u8-list->uint ((ixp-read ixp) width)))
|
||||||
|
|
||||||
|
(define (read-packet ixp)
|
||||||
|
(let* ((len (read-uint 4 ixp))
|
||||||
|
(msgnum (read-uint 1 ixp))
|
||||||
|
(spec (spec-by-num msgnum))
|
||||||
|
(fmt (caddr spec))
|
||||||
|
(datum ((ixp-read ixp) (- len 5))))
|
||||||
|
(cons (car spec)
|
||||||
|
(unpack fmt datum))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; 9p record
|
||||||
|
;;
|
||||||
|
;; This is how I deal with the fact that no two scheme implementations
|
||||||
|
;; have the same socket API. There are no SRFIs for sockets so that's
|
||||||
|
;; not likely to change in the near future.
|
||||||
|
;;
|
||||||
|
;; You create one of these with (make-ixp read-u8-list write-u8-list).
|
||||||
|
;; read-u8-list should one argument, count, and return a list of length
|
||||||
|
;; count of octets (u8s) read from the socket. write-u8-list takes one
|
||||||
|
;; argument, l (a u8-list), and writes that to the socket.
|
||||||
|
;;
|
||||||
|
(define-record-type ixp
|
||||||
|
(make-ixp read-u8-list write-u8-list)
|
||||||
|
ixp?
|
||||||
|
(read-u8-list ixp-read)
|
||||||
|
(write-u8-list ixp-write))
|
||||||
|
|
||||||
|
|
||||||
|
(define (ixp-transaction ixp type . args)
|
||||||
|
(apply write-packet (append (list ixp type 1) args))
|
||||||
|
(let ((ret (read-packet ixp)))
|
||||||
|
(if (equal? (car ret) 'RError)
|
||||||
|
(error (format "IXP Recieve: ~a" ret))
|
||||||
|
ret)))
|
||||||
|
|
||||||
|
;; Rewriting this procedure should be all you need to do in order to
|
||||||
|
;; port this code.
|
||||||
|
(define (gauche-with-ixp socket proc)
|
||||||
|
(call-with-client-socket socket
|
||||||
|
(lambda (in out)
|
||||||
|
(let ((ixp (make-ixp
|
||||||
|
(lambda (count)
|
||||||
|
(let ((vec (make-u8vector count)))
|
||||||
|
(if (not (equal? (read-block! vec in) count))
|
||||||
|
(error "read-octets: short read")
|
||||||
|
(u8vector->list vec))))
|
||||||
|
(lambda (u8-list)
|
||||||
|
(write-block (list->u8vector u8-list) out)
|
||||||
|
(flush out)))))
|
||||||
|
(proc ixp)))))
|
||||||
|
|
||||||
|
|
||||||
|
(define (main args)
|
||||||
|
(gauche-with-ixp (make-client-socket 'unix "/tmp/ns.neale.:0/wmii")
|
||||||
|
(lambda (ixp)
|
||||||
|
(let ((root-fid #xf00fb1ba)
|
||||||
|
(fid 1)
|
||||||
|
(username "the dink")
|
||||||
|
(filename "event")
|
||||||
|
(data "hello\n"))
|
||||||
|
(ixp-transaction ixp 'TVersion 4096 "9P2000")
|
||||||
|
(ixp-transaction ixp 'TAttach root-fid #xffffffff username "")
|
||||||
|
|
||||||
|
(ixp-transaction ixp 'TWalk root-fid fid (list filename))
|
||||||
|
(ixp-transaction ixp 'TOpen fid 1)
|
||||||
|
(ixp-transaction ixp 'TWrite fid 0 (list data))
|
||||||
|
(ixp-transaction ixp 'TClunk fid)
|
||||||
|
|
||||||
|
(ixp-transaction ixp 'TWalk root-fid fid (list filename))
|
||||||
|
(ixp-transaction ixp 'TOpen fid 0)
|
||||||
|
(write
|
||||||
|
(let ((cl (caddr (ixp-transaction ixp 'TRead fid 0 4096))))
|
||||||
|
(ixp-transaction ixp 'TClunk fid)
|
||||||
|
(u8-list->string cl)))
|
||||||
|
(newline))))
|
||||||
|
0)
|
|
@ -0,0 +1,18 @@
|
||||||
|
all: dwm-config.h spamfairy.py mail-expire.sh gourmet.sh deliver.sh
|
||||||
|
|
||||||
|
COPY = cp $< $@
|
||||||
|
|
||||||
|
dwm-config.h: $(HOME)/opt/src/dwm/config.h
|
||||||
|
$(COPY)
|
||||||
|
|
||||||
|
spamfairy.py: /usr/local/bin/spamfairy
|
||||||
|
$(COPY)
|
||||||
|
|
||||||
|
mail-expire.sh: /usr/local/bin/mail-expire
|
||||||
|
$(COPY)
|
||||||
|
|
||||||
|
gourmet.sh: /usr/local/bin/gourmet
|
||||||
|
$(COPY)
|
||||||
|
|
||||||
|
deliver.sh: /usr/local/bin/deliver
|
||||||
|
$(COPY)
|
|
@ -0,0 +1,38 @@
|
||||||
|
#! /bin/sh -x
|
||||||
|
|
||||||
|
JUNK=$HOME/Maildir/.Junk
|
||||||
|
|
||||||
|
deliver () {
|
||||||
|
fn=`date +%s`.$$.`hostname`
|
||||||
|
mv $1 $2/tmp/$fn
|
||||||
|
mv $2/tmp/$fn $2/new/
|
||||||
|
chmod 600 $2/new/$fn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d $JUNK ]; then
|
||||||
|
tmp=`tempfile`
|
||||||
|
trap "rm -f $tmp" 0
|
||||||
|
|
||||||
|
bogofilter -p -u > $tmp
|
||||||
|
case $? in
|
||||||
|
0) # spam
|
||||||
|
deliver $tmp $JUNK
|
||||||
|
;;
|
||||||
|
1) # ham
|
||||||
|
/usr/lib/dovecot/deliver < $tmp
|
||||||
|
;;
|
||||||
|
2) # unsure
|
||||||
|
for d in .maybe -maybe; do
|
||||||
|
if [ -d $JUNK$d ]; then
|
||||||
|
deliver $tmp $JUNK$d
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Fall-through
|
||||||
|
/usr/lib/dovecot/deliver < $tmp
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
exec /usr/lib/dovecot/deliver
|
||||||
|
fi
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Description: Neale's DWM config
|
||||||
|
* Author: Neale Pickett <neale@woozle.org>
|
||||||
|
* Time-stamp: <2008-03-31 11:52:28 neale>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TERM "rxvt"
|
||||||
|
|
||||||
|
/* appearance */
|
||||||
|
#define BORDERPX 2
|
||||||
|
#define FONT "-adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*"
|
||||||
|
#define NORMBORDERCOLOR "#cfdde6"
|
||||||
|
#define SELBORDERCOLOR "#80d0ff"
|
||||||
|
#define NORMBGCOLOR "#69668b"
|
||||||
|
#define NORMFGCOLOR "#d3d1e6"
|
||||||
|
#define SELBGCOLOR "#ccb48f"
|
||||||
|
#define SELFGCOLOR "#000000"
|
||||||
|
|
||||||
|
/* tagging */
|
||||||
|
const char tags[][MAXTAGLEN] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||||
|
|
||||||
|
Rule rules[] = {
|
||||||
|
/* class:instance:title substr tags ref isfloating */
|
||||||
|
{ "Gimp", tags[5], True },
|
||||||
|
{ "KNetworkManager", tags[8], False },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* layout(s) */
|
||||||
|
#define RESIZEHINTS True /* False - respect size hints in tiled resizals */
|
||||||
|
#define SNAP 32 /* snap pixel */
|
||||||
|
|
||||||
|
Layout layouts[] = {
|
||||||
|
/* symbol function isfloating */
|
||||||
|
{ "[]=", tilev, False }, /* first entry is default */
|
||||||
|
{ "><>", floating, True },
|
||||||
|
{ "[M]", monocle, False },
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *layoutcycle[] = {"[]=", "[M]"};
|
||||||
|
|
||||||
|
void
|
||||||
|
nextlayout(const char *arg)
|
||||||
|
{
|
||||||
|
static int which = 0;
|
||||||
|
|
||||||
|
which = (which + 1) % LENGTH(layoutcycle);
|
||||||
|
setlayout(layoutcycle[which]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
restart(const char *arg)
|
||||||
|
{
|
||||||
|
if (arg) {
|
||||||
|
execlp(arg, arg, NULL);
|
||||||
|
} else {
|
||||||
|
execlp("dwm", "dwm", NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* key definitions */
|
||||||
|
#define MODKEY Mod4Mask
|
||||||
|
Key keys[] = {
|
||||||
|
/* modifier key function argument */
|
||||||
|
{ MODKEY, XK_a, spawn,
|
||||||
|
"exec dmenu_run -fn '"FONT"' -nb '"NORMBGCOLOR"' -nf '"NORMFGCOLOR"' -sb '"SELBGCOLOR"' -sf '"SELFGCOLOR"'" },
|
||||||
|
{ MODKEY|ShiftMask, XK_Return, spawn, "exec " TERM },
|
||||||
|
{ MODKEY, XK_l, spawn, "exec screenlock" },
|
||||||
|
{ MODKEY, XK_m, spawn, "exec " TERM " -e ncmpc" },
|
||||||
|
{ MODKEY, XK_w, spawn, "exec mpc prev" },
|
||||||
|
{ MODKEY, XK_v, spawn, "exec mpc toggle" },
|
||||||
|
{ MODKEY, XK_z, spawn, "exec mpc next" },
|
||||||
|
|
||||||
|
{ MODKEY, XK_n, focusnext, NULL },
|
||||||
|
{ MODKEY, XK_t, focusprev, NULL },
|
||||||
|
{ MODKEY, XK_Return, zoom, NULL },
|
||||||
|
{ MODKEY, XK_equal, nextlayout, NULL },
|
||||||
|
{ MODKEY|ShiftMask, XK_equal, setlayout, "><>" },
|
||||||
|
{ MODKEY|ShiftMask, XK_space, togglefloating, NULL },
|
||||||
|
{ MODKEY, XK_grave, killclient, NULL },
|
||||||
|
{ MODKEY, XK_0, view, NULL },
|
||||||
|
{ MODKEY, XK_1, view, tags[0] },
|
||||||
|
{ MODKEY, XK_2, view, tags[1] },
|
||||||
|
{ MODKEY, XK_3, view, tags[2] },
|
||||||
|
{ MODKEY, XK_4, view, tags[3] },
|
||||||
|
{ MODKEY, XK_5, view, tags[4] },
|
||||||
|
{ MODKEY, XK_6, view, tags[5] },
|
||||||
|
{ MODKEY, XK_7, view, tags[6] },
|
||||||
|
{ MODKEY, XK_8, view, tags[7] },
|
||||||
|
{ MODKEY, XK_9, view, tags[8] },
|
||||||
|
{ MODKEY|ShiftMask, XK_0, tag, NULL },
|
||||||
|
{ MODKEY|ShiftMask, XK_1, tag, tags[0] },
|
||||||
|
{ MODKEY|ShiftMask, XK_2, tag, tags[1] },
|
||||||
|
{ MODKEY|ShiftMask, XK_3, tag, tags[2] },
|
||||||
|
{ MODKEY|ShiftMask, XK_4, tag, tags[3] },
|
||||||
|
{ MODKEY|ShiftMask, XK_5, tag, tags[4] },
|
||||||
|
{ MODKEY|ShiftMask, XK_6, tag, tags[5] },
|
||||||
|
{ MODKEY|ShiftMask, XK_7, tag, tags[6] },
|
||||||
|
{ MODKEY|ShiftMask, XK_8, tag, tags[7] },
|
||||||
|
{ MODKEY|ShiftMask, XK_9, tag, tags[8] },
|
||||||
|
{ MODKEY, XK_q, restart, NULL },
|
||||||
|
{ MODKEY|ShiftMask, XK_q, quit, NULL },
|
||||||
|
};
|
|
@ -0,0 +1,389 @@
|
||||||
|
(** A stupid adventure game
|
||||||
|
|
||||||
|
Nick wanted an adventure game framework. Here ya go, Nick.
|
||||||
|
|
||||||
|
Compile this with
|
||||||
|
ocamlc -o foo foo.ml
|
||||||
|
|
||||||
|
@author Neale Pickett <neale@woozle.org>
|
||||||
|
|
||||||
|
*)
|
||||||
|
|
||||||
|
(** Directions *)
|
||||||
|
type direction = North | South | East | West | Up | Down | Emad
|
||||||
|
|
||||||
|
(** An item *)
|
||||||
|
type item = {
|
||||||
|
names: string list;
|
||||||
|
i_full_name: string;
|
||||||
|
i_description: string;
|
||||||
|
actions: (string * (item -> unit)) list;
|
||||||
|
}
|
||||||
|
|
||||||
|
(** A room. Contents are mutable so that the player can pick stuff
|
||||||
|
up. Exits are mutable so that I can link them back and forth *)
|
||||||
|
type room = {
|
||||||
|
r_full_name: string;
|
||||||
|
r_description: string;
|
||||||
|
mutable contents: item list;
|
||||||
|
mutable exits: (direction * room) list;
|
||||||
|
}
|
||||||
|
|
||||||
|
(** The player *)
|
||||||
|
type player = {
|
||||||
|
mutable carrying: item list;
|
||||||
|
mutable location: room;
|
||||||
|
}
|
||||||
|
|
||||||
|
let player = {
|
||||||
|
carrying = [];
|
||||||
|
location = {
|
||||||
|
r_full_name = "Nowhere";
|
||||||
|
r_description = "Non-descript";
|
||||||
|
contents = [];
|
||||||
|
exits = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*
|
||||||
|
*
|
||||||
|
* General functions
|
||||||
|
*
|
||||||
|
*)
|
||||||
|
|
||||||
|
(** Relocate something
|
||||||
|
|
||||||
|
Note that this does no checks to make sure the item is actually in
|
||||||
|
src!
|
||||||
|
|
||||||
|
@param i item to relocate
|
||||||
|
@param src where it comes from
|
||||||
|
@param dst where it goes
|
||||||
|
@return (new src, new dst)
|
||||||
|
*)
|
||||||
|
let move i src dst =
|
||||||
|
(List.filter ((!=) i) src,
|
||||||
|
i :: dst)
|
||||||
|
|
||||||
|
|
||||||
|
(** Is the player carrying an item? *)
|
||||||
|
let carrying p i =
|
||||||
|
List.exists ((==) i) p.carrying
|
||||||
|
|
||||||
|
(** Drop something *)
|
||||||
|
let drop i =
|
||||||
|
if carrying player i then
|
||||||
|
let carrying, contents = move i player.carrying player.location.contents in
|
||||||
|
player.carrying <- carrying;
|
||||||
|
player.location.contents <- contents;
|
||||||
|
else
|
||||||
|
raise (Failure ("You're not carrying " ^ i.i_full_name ^ "!"))
|
||||||
|
|
||||||
|
|
||||||
|
(** Pick something up *)
|
||||||
|
let get i =
|
||||||
|
if carrying player i then
|
||||||
|
raise (Failure ("You already have " ^ i.i_full_name ^ "!"))
|
||||||
|
else if List.exists ((==) i) player.location.contents then
|
||||||
|
let contents, carrying = move i player.location.contents player.carrying in
|
||||||
|
player.carrying <- carrying;
|
||||||
|
player.location.contents <- contents;
|
||||||
|
else
|
||||||
|
raise (Failure ("You don't see " ^ i.i_full_name ^ " here!"))
|
||||||
|
|
||||||
|
|
||||||
|
(** Describe an item *)
|
||||||
|
let describe i =
|
||||||
|
print_endline i.i_description
|
||||||
|
|
||||||
|
(*
|
||||||
|
*
|
||||||
|
* Rooms
|
||||||
|
*
|
||||||
|
* There's really no need to clutter up the global namespace here,
|
||||||
|
* since all I really need is "start_location" to set the starting
|
||||||
|
* location.
|
||||||
|
*
|
||||||
|
*)
|
||||||
|
let start_location =
|
||||||
|
let bigroom = { (* Big room *)
|
||||||
|
r_full_name = "The big room";
|
||||||
|
r_description = "This is that big room just outside the computer lab " ^
|
||||||
|
"with the blue ceiling. It smells fresh, yet dirty at the same time. " ^
|
||||||
|
"Off to the north is a grassy knoll.";
|
||||||
|
contents = [
|
||||||
|
{ (* lantern *)
|
||||||
|
names = ["brass lantern"; "brass"; "lantern"; "lamp"; "light"];
|
||||||
|
i_full_name = "a brass lantern";
|
||||||
|
i_description = "This rusty lantern looks like it's been used in " ^
|
||||||
|
"one too many adventure games.";
|
||||||
|
actions = [
|
||||||
|
("rub",
|
||||||
|
fun self ->
|
||||||
|
print_endline "Nothing happens.";
|
||||||
|
);
|
||||||
|
("kick",
|
||||||
|
fun self ->
|
||||||
|
print_endline "There'll be a hot time in the old town tonight!";
|
||||||
|
)
|
||||||
|
]
|
||||||
|
};
|
||||||
|
{ (* stone *)
|
||||||
|
names = ["smooth"; "stone"; "rock"];
|
||||||
|
i_full_name = "a smooth stone";
|
||||||
|
i_description = "This stone is smooth. Yes it is.";
|
||||||
|
actions = [
|
||||||
|
("rub",
|
||||||
|
fun self ->
|
||||||
|
print_endline "Your worries seem to vanish.";
|
||||||
|
);
|
||||||
|
("throw",
|
||||||
|
fun self ->
|
||||||
|
print_endline "You throw the stone.";
|
||||||
|
let carrying, contents = (move self player.carrying
|
||||||
|
player.location.contents) in
|
||||||
|
drop self;
|
||||||
|
print_endline "It lands a stone's throw away.";
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
exits = []
|
||||||
|
}
|
||||||
|
in
|
||||||
|
let knoll = { (* Grassy knoll *)
|
||||||
|
r_full_name = "Grassy knoll";
|
||||||
|
r_description = "You find yourself standing on the top of a " ^
|
||||||
|
"small mound. The green carpeting of grass beneath your feet " ^
|
||||||
|
"beckons kite-flying or frisbee-throwing. Near the bottom of the " ^
|
||||||
|
"mound is a funny-looking man intensely examining a teacup. He " ^
|
||||||
|
"is wearing a nametag reading \"Emad\".";
|
||||||
|
contents = [];
|
||||||
|
exits = []
|
||||||
|
}
|
||||||
|
in
|
||||||
|
let emad = {
|
||||||
|
r_full_name = "Emad"; (* Emad *)
|
||||||
|
r_description = "You are in Emad. How unpleasant.";
|
||||||
|
contents = [
|
||||||
|
{ (* ham sandwich *)
|
||||||
|
names = ["ham and bacon sandwich"; "bacon sandwich"; "ham sandwich";
|
||||||
|
"sandwich"; "ham"; "bacon"];
|
||||||
|
i_full_name = "a delicious ham and bacon sandwich";
|
||||||
|
i_description = "Named after John Montagu, fourth Earl of Sandwich, " ^
|
||||||
|
"this savory instance is comprised of ham and bacon, \"sandwiched\" " ^
|
||||||
|
"as it were between two slices of whole-wheat bread.";
|
||||||
|
actions = [
|
||||||
|
("eat",
|
||||||
|
fun self ->
|
||||||
|
print_endline "A little acidic, but not bad."
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
exits = []
|
||||||
|
}
|
||||||
|
in
|
||||||
|
begin
|
||||||
|
bigroom.exits <- [(North, knoll)];
|
||||||
|
knoll.exits <- [(South, bigroom);
|
||||||
|
(Emad, emad)];
|
||||||
|
emad.exits <- [(Up, knoll)];
|
||||||
|
bigroom
|
||||||
|
end
|
||||||
|
|
||||||
|
let describe_room r =
|
||||||
|
let rec display_items = function
|
||||||
|
| [] ->
|
||||||
|
" nothing"
|
||||||
|
| [i] -> (* just one *)
|
||||||
|
(" " ^ i.i_full_name)
|
||||||
|
| [i; j] -> (* two *)
|
||||||
|
(" " ^ i.i_full_name ^
|
||||||
|
", and " ^ j.i_full_name)
|
||||||
|
| i :: j :: tl -> (* multiple *)
|
||||||
|
(" " ^ i.i_full_name ^ "," ^
|
||||||
|
display_items (j :: tl))
|
||||||
|
in
|
||||||
|
print_endline "";
|
||||||
|
print_endline r.r_full_name;
|
||||||
|
print_endline "";
|
||||||
|
print_endline r.r_description;
|
||||||
|
if (r.contents = []) then
|
||||||
|
print_endline ""
|
||||||
|
else
|
||||||
|
print_endline ("You see" ^ (display_items r.contents) ^ " here.")
|
||||||
|
|
||||||
|
|
||||||
|
(*
|
||||||
|
*
|
||||||
|
* Player functions
|
||||||
|
*
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
|
let rec find_item name l =
|
||||||
|
match (name, l) with
|
||||||
|
| _, [] ->
|
||||||
|
raise Not_found
|
||||||
|
| name, i :: tl ->
|
||||||
|
if List.exists ((=) name) i.names then
|
||||||
|
i
|
||||||
|
else
|
||||||
|
find_item name tl
|
||||||
|
|
||||||
|
let rec find_action action l =
|
||||||
|
match (action, l) with
|
||||||
|
| _, [] ->
|
||||||
|
raise Not_found
|
||||||
|
| action, (name, func) :: tl ->
|
||||||
|
if (action = name) then
|
||||||
|
func
|
||||||
|
else
|
||||||
|
find_action action tl
|
||||||
|
|
||||||
|
(** Apply action to name
|
||||||
|
|
||||||
|
@param action What to do
|
||||||
|
@param name What to do it to
|
||||||
|
*)
|
||||||
|
let apply_action action name =
|
||||||
|
match action with
|
||||||
|
| "describe" | "look" | "l" ->
|
||||||
|
let i = try
|
||||||
|
find_item name player.carrying
|
||||||
|
with Not_found ->
|
||||||
|
try
|
||||||
|
find_item name player.location.contents
|
||||||
|
with Not_found ->
|
||||||
|
raise (Failure "You're not carrying that.")
|
||||||
|
in
|
||||||
|
describe i
|
||||||
|
| "take" | "get" ->
|
||||||
|
let i = try
|
||||||
|
find_item name player.location.contents
|
||||||
|
with Not_found ->
|
||||||
|
raise (Failure "You don't see that here.")
|
||||||
|
in
|
||||||
|
get i;
|
||||||
|
print_endline "Taken."
|
||||||
|
| "drop" ->
|
||||||
|
let i = try
|
||||||
|
find_item name player.carrying
|
||||||
|
with Not_found ->
|
||||||
|
raise (Failure "You're not carrying that.")
|
||||||
|
in
|
||||||
|
drop i;
|
||||||
|
print_endline "Dropped."
|
||||||
|
| _ ->
|
||||||
|
let i = try
|
||||||
|
find_item name player.carrying
|
||||||
|
with Not_found ->
|
||||||
|
raise (Failure "You don't have that.")
|
||||||
|
in
|
||||||
|
let a = try
|
||||||
|
find_action action i.actions
|
||||||
|
with Not_found ->
|
||||||
|
raise (Failure ("You can't do that to " ^ i.i_full_name ^ "."))
|
||||||
|
in
|
||||||
|
a i
|
||||||
|
|
||||||
|
(** Move the player
|
||||||
|
|
||||||
|
@param dir direction
|
||||||
|
*)
|
||||||
|
let go dir =
|
||||||
|
let moveout (d, room) =
|
||||||
|
if d = dir then
|
||||||
|
begin
|
||||||
|
player.location <- room;
|
||||||
|
describe_room player.location;
|
||||||
|
end
|
||||||
|
in
|
||||||
|
let here = player.location in
|
||||||
|
List.iter moveout player.location.exits;
|
||||||
|
if here == player.location then
|
||||||
|
raise (Failure "You can't get there from here.")
|
||||||
|
|
||||||
|
(** Display inventory
|
||||||
|
*)
|
||||||
|
let inventory () =
|
||||||
|
let print_item i =
|
||||||
|
print_endline ("* " ^ i.i_full_name)
|
||||||
|
in
|
||||||
|
if (player.carrying = []) then
|
||||||
|
print_endline "You are empty-handed."
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
print_endline "You are carrying: ";
|
||||||
|
List.iter print_item player.carrying
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
(** Do something
|
||||||
|
|
||||||
|
@param action What to do
|
||||||
|
*)
|
||||||
|
let do_action action =
|
||||||
|
match action with
|
||||||
|
| "inventory" | "inv" | "i" ->
|
||||||
|
inventory ()
|
||||||
|
| "look" | "l" ->
|
||||||
|
describe_room player.location
|
||||||
|
| "north" | "n" ->
|
||||||
|
go North
|
||||||
|
| "south" | "s" ->
|
||||||
|
go South
|
||||||
|
| "east" | "e" ->
|
||||||
|
go East
|
||||||
|
| "west" | "w" ->
|
||||||
|
go West
|
||||||
|
| "up" | "u" ->
|
||||||
|
go Up
|
||||||
|
| "down" | "d" ->
|
||||||
|
go Down
|
||||||
|
| "emad" ->
|
||||||
|
go Emad
|
||||||
|
| _ ->
|
||||||
|
print_endline "I'm sorry, Dave, I'm afraid I can't do that."
|
||||||
|
|
||||||
|
(** Parse a line
|
||||||
|
|
||||||
|
@param str the line to parse
|
||||||
|
*)
|
||||||
|
let parse str =
|
||||||
|
try
|
||||||
|
let action, name =
|
||||||
|
let pos = String.index str ' ' in
|
||||||
|
(String.sub str 0 pos,
|
||||||
|
String.sub str (pos + 1) ((String.length str) - pos - 1))
|
||||||
|
in
|
||||||
|
apply_action action name
|
||||||
|
with Not_found ->
|
||||||
|
do_action str
|
||||||
|
|
||||||
|
(** Read-Eval-Print loop
|
||||||
|
*)
|
||||||
|
let rec repl () =
|
||||||
|
let line = read_line () in
|
||||||
|
print_endline "";
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
parse (String.lowercase line)
|
||||||
|
with Failure str ->
|
||||||
|
print_endline str
|
||||||
|
end;
|
||||||
|
repl ()
|
||||||
|
|
||||||
|
let main () =
|
||||||
|
player.location <- start_location;
|
||||||
|
describe_room player.location;
|
||||||
|
repl ()
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
try
|
||||||
|
main ()
|
||||||
|
with End_of_file ->
|
||||||
|
()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
frm="$1"
|
||||||
|
ext="$2"
|
||||||
|
base="${HOME}/.gourmet"
|
||||||
|
|
||||||
|
num="`echo $ext | sed -n 's/^\([0-9]*\)-.*/\1/p'`"
|
||||||
|
|
||||||
|
if [ -z "$num" ]; then
|
||||||
|
# No number; not a gourmet address
|
||||||
|
exec cat
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -d "$base" ]; then
|
||||||
|
mkdir "$base"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$base/$ext" ]; then
|
||||||
|
num="`cat $base/$ext`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
left=`expr "$num" - 1`
|
||||||
|
echo $left > "$base/$ext"
|
||||||
|
|
||||||
|
if [ "$num" -gt 0 ]; then
|
||||||
|
exec awk "
|
||||||
|
{
|
||||||
|
if (! p && /^Subject: /) {
|
||||||
|
sub(\"^Subject: \", \"Subject: <$ext: $left left> \");
|
||||||
|
p = 1;
|
||||||
|
}
|
||||||
|
print;
|
||||||
|
}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Must have sent too many, return permanent failure code
|
||||||
|
echo "Address has self-destructed." 1>&2
|
||||||
|
exit 69
|
Binary file not shown.
|
@ -0,0 +1,86 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
description = '''All Maildir++ folders are checked for old mail. Any messages older than
|
||||||
|
DAYS days than the newest message in a folder are removed. If there is
|
||||||
|
a file named"expire" in a maildir, its contents will be used instead of
|
||||||
|
DAYS for that folder. You can put "12345678" in the "expire" file to
|
||||||
|
set an expiration of 1413 years, which is probably longer than you will
|
||||||
|
care about the mail folder.
|
||||||
|
|
||||||
|
Specify a path in MAILDIR to check a specific Maildir. Subfolders will
|
||||||
|
not be checked in this case.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import optparse
|
||||||
|
|
||||||
|
progname = sys.argv[0]
|
||||||
|
debug = 0
|
||||||
|
|
||||||
|
def expire(dirname, days):
|
||||||
|
expire = "%s/expire" % dirname
|
||||||
|
if os.path.exists(expire):
|
||||||
|
days = int(open(expire).read())
|
||||||
|
secs = long(days) * 24 * 60 * 60
|
||||||
|
messages = glob.glob("%s/cur/*" % dirname)
|
||||||
|
messages += glob.glob("%s/new/*" % dirname)
|
||||||
|
if not messages:
|
||||||
|
return
|
||||||
|
messages = [(os.path.getmtime(m), m) for m in messages]
|
||||||
|
messages.sort()
|
||||||
|
latest = messages[-1][0]
|
||||||
|
for m in messages:
|
||||||
|
if (m[0] + secs) < latest:
|
||||||
|
try:
|
||||||
|
_, flags = m[1].split(',')
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if 'S' in flags:
|
||||||
|
# Only if the mail's been seen
|
||||||
|
if debug:
|
||||||
|
print m[1]
|
||||||
|
else:
|
||||||
|
os.remove(m[1])
|
||||||
|
|
||||||
|
def usage(err=None):
|
||||||
|
if err:
|
||||||
|
print "Error: %s" % err
|
||||||
|
print
|
||||||
|
print __doc__ % globals()
|
||||||
|
if err:
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import optparse
|
||||||
|
global debug
|
||||||
|
|
||||||
|
parser = optparse.OptionParser(usage='usage: %prog [options] [MAILDIR ...]',
|
||||||
|
description=description)
|
||||||
|
parser.add_option('-d', '--debug', dest='debug', action='store_true',
|
||||||
|
help="run in debug mode (don't do anything)")
|
||||||
|
parser.add_option('-t', '--time', dest='days', metavar='DAYS', type='int',
|
||||||
|
default=90,
|
||||||
|
help="Mail older than DAYS days will be removed (default 90)")
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
debug = options.debug
|
||||||
|
|
||||||
|
if (len(args) == 0):
|
||||||
|
base = os.path.expanduser("~/Maildir")
|
||||||
|
files = [os.path.join(base, x) for x in os.listdir(base) if x[0] == '.']
|
||||||
|
dirs = [f for f in files if os.path.isdir(f)]
|
||||||
|
dirs.append(base)
|
||||||
|
else:
|
||||||
|
dirs = args
|
||||||
|
|
||||||
|
for d in dirs:
|
||||||
|
expire(d, options.days)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,190 @@
|
||||||
|
#! /usr/bin/python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import time
|
||||||
|
from sets import Set
|
||||||
|
|
||||||
|
bogosity = {'U': "Unsure",
|
||||||
|
'S': "Spam",
|
||||||
|
'H': "Ham"}
|
||||||
|
|
||||||
|
def classification(filename):
|
||||||
|
f = file(filename)
|
||||||
|
for l in f:
|
||||||
|
if l.startswith('X-Bogosity: '):
|
||||||
|
return l[12]
|
||||||
|
elif not l.strip():
|
||||||
|
return None
|
||||||
|
|
||||||
|
def reclassify(oldtype, newtype, filenames):
|
||||||
|
cmd = 'bogofilter -b -v'
|
||||||
|
t = oldtype + newtype
|
||||||
|
if t == 'SH':
|
||||||
|
cmd += ' -Sn'
|
||||||
|
elif t == 'HS':
|
||||||
|
cmd += ' -Ns'
|
||||||
|
elif t == 'UH':
|
||||||
|
cmd += ' -n'
|
||||||
|
elif t == 'US':
|
||||||
|
cmd += ' -s'
|
||||||
|
else:
|
||||||
|
raise ValueError('Unable to reclassify %s' % t)
|
||||||
|
|
||||||
|
cmd += ' 2>/dev/null'
|
||||||
|
|
||||||
|
# Reclassify them
|
||||||
|
f = os.popen(cmd, 'w')
|
||||||
|
for fn in filenames:
|
||||||
|
f.write('%s\n' % fn)
|
||||||
|
ret = f.close()
|
||||||
|
if ret:
|
||||||
|
raise IOError("bogofilter puked on %s (%r)" % (fn, ret))
|
||||||
|
|
||||||
|
# Add new bogosity header
|
||||||
|
for fn in filenames:
|
||||||
|
base, filename = os.path.split(fn)
|
||||||
|
folder, _ = os.path.split(base)
|
||||||
|
tmpfn = os.path.join(folder, 'tmp', filename)
|
||||||
|
|
||||||
|
inf = file(fn)
|
||||||
|
outf = file(tmpfn, 'w')
|
||||||
|
|
||||||
|
headered = False
|
||||||
|
for l in inf:
|
||||||
|
if l.startswith('X-Bogosity: '):
|
||||||
|
l = (('X-Bogosity: %s (Reclassified), was ' % bogosity[newtype])
|
||||||
|
+ l[12:])
|
||||||
|
headered = True
|
||||||
|
elif not l.strip() and not headered:
|
||||||
|
l = (('X-Bogosity: %s (Reclassified)\n' % bogosity[newtype]))
|
||||||
|
headered = True
|
||||||
|
outf.write(l)
|
||||||
|
os.rename(tmpfn, fn)
|
||||||
|
|
||||||
|
def visit(path, folder_type):
|
||||||
|
# Read in list of already-processed files
|
||||||
|
visited = os.path.join(path, 'spamfairy-visited')
|
||||||
|
oldfiles = Set()
|
||||||
|
try:
|
||||||
|
f = file(visited)
|
||||||
|
for l in f:
|
||||||
|
oldfiles.add(l.strip())
|
||||||
|
f.close()
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if folder_type == 'S':
|
||||||
|
subdirs = ('cur', 'new')
|
||||||
|
else:
|
||||||
|
subdirs = ('cur',)
|
||||||
|
|
||||||
|
for sd in subdirs:
|
||||||
|
# Read in list of current files in the directory
|
||||||
|
root = os.path.join(path, sd)
|
||||||
|
files = Set(os.listdir(root))
|
||||||
|
|
||||||
|
# We only consider the difference
|
||||||
|
todo = files - oldfiles
|
||||||
|
|
||||||
|
# Check new messages for reclassification
|
||||||
|
reclass = {} # {oldtype: [filenames]}
|
||||||
|
for fn in todo:
|
||||||
|
fn = os.path.join(root, fn)
|
||||||
|
c = classification(fn)
|
||||||
|
if not c:
|
||||||
|
continue
|
||||||
|
if c != folder_type:
|
||||||
|
l = reclass.setdefault(c, [])
|
||||||
|
l.append(fn)
|
||||||
|
|
||||||
|
# Reclassify anything that needs it
|
||||||
|
for c, filenames in reclass.iteritems():
|
||||||
|
try:
|
||||||
|
reclassify(c, folder_type, filenames)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Write out new list of processed files
|
||||||
|
new_visited = "%s.%d" % (visited, os.getpid())
|
||||||
|
f = file(new_visited, 'w')
|
||||||
|
for fn in files:
|
||||||
|
f.write('%s\n' % fn)
|
||||||
|
f.close()
|
||||||
|
os.rename(new_visited, visited)
|
||||||
|
|
||||||
|
|
||||||
|
def get_folder_type(path):
|
||||||
|
"""Auto-detect type of a maildir.
|
||||||
|
|
||||||
|
You can have the following files in your maildir:
|
||||||
|
spamfairy-ignore : skip this folder entirely
|
||||||
|
spamfairy-spam : treat this folder as spam
|
||||||
|
spamfairy-ham : treat this folder as ham
|
||||||
|
|
||||||
|
Otherwise, check the name of the folder:
|
||||||
|
Trash : skip
|
||||||
|
Drafts : skip
|
||||||
|
Sent : skip
|
||||||
|
Unsure : skip
|
||||||
|
Junk.maybe : skip
|
||||||
|
Junk : spam
|
||||||
|
|
||||||
|
Otherwise, treat it as a ham folder.
|
||||||
|
|
||||||
|
Return values are None for skip, 'S' for spam, 'H' for ham
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
path = os.path.realpath(path)
|
||||||
|
_, folder = os.path.split(path)
|
||||||
|
folder = folder.lower()
|
||||||
|
if os.path.exists(os.path.join(path, 'spamfairy-ignore')):
|
||||||
|
return None
|
||||||
|
elif os.path.exists(os.path.join(path, 'spamfairy-ham')):
|
||||||
|
return 'H'
|
||||||
|
elif os.path.exists(os.path.join(path, 'spamfairy-spam')):
|
||||||
|
return 'S'
|
||||||
|
elif folder in ('.sent', '.drafts', '.trash', '.unsure', '.junk.maybe'):
|
||||||
|
return None
|
||||||
|
elif folder in ('.junk', '.spam'):
|
||||||
|
return 'S'
|
||||||
|
else:
|
||||||
|
return 'H'
|
||||||
|
|
||||||
|
|
||||||
|
def sprinkle(folders):
|
||||||
|
"""Sprinkle magic spamfairy dust on the given folders"""
|
||||||
|
|
||||||
|
if not folders:
|
||||||
|
maildir = os.path.expanduser(os.path.join('~', 'Maildir'))
|
||||||
|
folders = [maildir]
|
||||||
|
folders += glob.glob(os.path.join(maildir, '.??*'))
|
||||||
|
|
||||||
|
# Only do maildirs
|
||||||
|
folders = filter(lambda d: os.path.isdir(os.path.join(d, 'cur')),
|
||||||
|
folders)
|
||||||
|
|
||||||
|
for path in folders:
|
||||||
|
folder_type = get_folder_type(path)
|
||||||
|
if not folder_type:
|
||||||
|
continue
|
||||||
|
visit(path, folder_type)
|
||||||
|
|
||||||
|
def compact(wordlist):
|
||||||
|
"""Compact bogofilter database"""
|
||||||
|
otime = os.path.getmtime(wordlist)
|
||||||
|
now = time.time()
|
||||||
|
if now - otime > 60*60*24*11:
|
||||||
|
new = '%s.new' % wordlist
|
||||||
|
ret = os.system('bogoutil -d %s | bogoutil -l %s' % (wordlist, new))
|
||||||
|
if not ret:
|
||||||
|
os.rename(new, wordlist)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
wordlist = os.path.expanduser(os.path.join('~', '.bogofilter', 'wordlist.db'))
|
||||||
|
if os.path.exists(wordlist):
|
||||||
|
sprinkle(sys.argv[1:])
|
||||||
|
compact(wordlist)
|
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
[[!meta title="Photobob"]]
|
[[!meta title="Photobob: Web photo albums"]]
|
||||||
|
|
||||||
I don't have a lot to say about photobob. It's the 7th or so photo
|
I don't have a lot to say about photobob. It's the 7th or so photo
|
||||||
album package I've written, and probably the best. You just put
|
album package I've written, and probably the best. You just put
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
index.html: - 12dab84c3ad5b0dba380c051471c14d6 - -
|
||||||
|
index.ptml: 1042003175 - 39a0ed00cb554392574b5955dbdc0af9 -
|
||||||
|
links.html: - 962d7fd4c69ea67cfeb1d0cc88e892a6 - -
|
||||||
|
links.ptml: 1041913579 - bd8a21beca5f285bf5e8ba50e86f340f -
|
|
@ -0,0 +1,13 @@
|
||||||
|
FILES = alice.ps banner.ps emergency-card.ps graph-paper.ps logo.ps
|
||||||
|
FILES += page_dimensions.ps resume.ps ruled-paper.ps skel.ps
|
||||||
|
|
||||||
|
TARGETS += index.html
|
||||||
|
|
||||||
|
include ../../Makefile
|
||||||
|
|
||||||
|
.INTERMEDIATE: index.txt
|
||||||
|
index.txt: index.dirlist $(FILES)
|
||||||
|
$(DIRLIST) $(FILES) > $@
|
||||||
|
|
||||||
|
%.png: %.ps
|
||||||
|
pstopnm -portrait -xmax 600 -stdout $< | pnmscale -x 300 | pnmtopng > $@
|
|
@ -0,0 +1,562 @@
|
||||||
|
%!PS-Adobe-2.0
|
||||||
|
%%Title: Alice in DIGITALand -- an example application of skel.ps
|
||||||
|
%%Creator: Neale Pickett <neale@lanl.gov>
|
||||||
|
%%CreationDate: Thu Nov 22 15:27:53 MST 1998
|
||||||
|
%% Time-stamp: <2002-10-22 09:34:24 neale>
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
/FontSize 12 def
|
||||||
|
|
||||||
|
/RegFont /Times-Roman def
|
||||||
|
/BoldFont /Times-Bold def
|
||||||
|
/ItalFont /Times-Italic def
|
||||||
|
|
||||||
|
/RegFSet RegFont findfont FontSize scalefont def
|
||||||
|
/BoldFSet BoldFont findfont FontSize scalefont def
|
||||||
|
/HeadFSet BoldFont findfont FontSize 1.1 mul scalefont def
|
||||||
|
/ItalFSet ItalFont findfont FontSize scalefont def
|
||||||
|
|
||||||
|
/SetRegFont { RegFSet setfont } def
|
||||||
|
/SetBoldFont { BoldFSet setfont } def
|
||||||
|
/SetHeadFont { HeadFSet setfont } def
|
||||||
|
/SetItalFont { ItalFSet setfont } def
|
||||||
|
|
||||||
|
/LM 50 def
|
||||||
|
/BM 50 def
|
||||||
|
/RM 580 LM sub def
|
||||||
|
/TM 760 BM sub def
|
||||||
|
|
||||||
|
/indentLevel 30 def
|
||||||
|
|
||||||
|
% Re-set the margins
|
||||||
|
/reset_margins {
|
||||||
|
/lm LM def
|
||||||
|
/rm RM def
|
||||||
|
} def
|
||||||
|
|
||||||
|
reset_margins
|
||||||
|
|
||||||
|
% Move down just a little
|
||||||
|
/down {
|
||||||
|
reset_margins
|
||||||
|
lm indentation add currentpoint exch pop
|
||||||
|
FontSize 3 div sub
|
||||||
|
moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move to the next line
|
||||||
|
/next {
|
||||||
|
reset_margins
|
||||||
|
lm indentation add currentpoint exch pop % LM Y
|
||||||
|
FontSize 1.1 mul sub % LM Y'
|
||||||
|
moveto
|
||||||
|
currentpoint exch pop
|
||||||
|
BM lt {
|
||||||
|
showpage
|
||||||
|
LM TM moveto
|
||||||
|
} {} ifelse
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move to the previous line
|
||||||
|
/prev {
|
||||||
|
lm indentation add currentpoint exch pop % LM Y
|
||||||
|
FontSize 1.1 mul add % LM Y'
|
||||||
|
moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Re-align the indentation
|
||||||
|
/align {
|
||||||
|
lm indentation add currentpoint exch pop moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Indent once
|
||||||
|
/indentation 0 def
|
||||||
|
/indent {
|
||||||
|
/indentation indentation indentLevel add def
|
||||||
|
align
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Deindent
|
||||||
|
/deindent {
|
||||||
|
/indentation indentation indentLevel sub def
|
||||||
|
align
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show left justified
|
||||||
|
/lshow {
|
||||||
|
gsave
|
||||||
|
% Set the left margin
|
||||||
|
dup
|
||||||
|
stringwidth pop
|
||||||
|
LM add
|
||||||
|
/lm exch def
|
||||||
|
|
||||||
|
currentpoint exch pop % (str) Y
|
||||||
|
LM exch % (str) x Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show centered
|
||||||
|
/cshow {
|
||||||
|
gsave
|
||||||
|
dup % (str) (str)
|
||||||
|
stringwidth pop % (str) x
|
||||||
|
2 div % (str) x/2
|
||||||
|
RM LM sub 2 div % (str) x/2 RM/2
|
||||||
|
exch sub LM add % (str) x'
|
||||||
|
currentpoint exch pop % (str) x' Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show right justified
|
||||||
|
/rshow {
|
||||||
|
gsave
|
||||||
|
dup % (str) (str)
|
||||||
|
stringwidth pop % (str) x
|
||||||
|
|
||||||
|
% set the right margin
|
||||||
|
dup
|
||||||
|
RM exch sub padwidth sub
|
||||||
|
/rm exch def
|
||||||
|
|
||||||
|
RM exch sub % (str) x'
|
||||||
|
currentpoint exch pop % (str) x' Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show in a bold font
|
||||||
|
/bshow {
|
||||||
|
SetBoldFont
|
||||||
|
show
|
||||||
|
SetRegFont
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show in a italics font
|
||||||
|
/ishow {
|
||||||
|
SetItalFont
|
||||||
|
show
|
||||||
|
SetRegFont
|
||||||
|
} def
|
||||||
|
|
||||||
|
% I totally stole this out of the blue book.
|
||||||
|
/wordbreak ( ) def
|
||||||
|
/BreakIntoLines {
|
||||||
|
/proc exch def
|
||||||
|
/linelength exch def
|
||||||
|
/textstring exch def
|
||||||
|
/curwidth exch def
|
||||||
|
|
||||||
|
/breakwidth wordbreak stringwidth pop def
|
||||||
|
/lastwordbreak 0 def
|
||||||
|
|
||||||
|
/startchar 0 def
|
||||||
|
/restoftext textstring def
|
||||||
|
|
||||||
|
{
|
||||||
|
restoftext wordbreak search
|
||||||
|
|
||||||
|
{
|
||||||
|
/done false def
|
||||||
|
} {
|
||||||
|
() exch
|
||||||
|
wordbreak exch
|
||||||
|
/done true def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
/nextword exch def pop
|
||||||
|
/restoftext exch def
|
||||||
|
/wordwidth nextword stringwidth pop def
|
||||||
|
|
||||||
|
curwidth wordwidth add linelength gt
|
||||||
|
{
|
||||||
|
textstring startchar
|
||||||
|
lastwordbreak startchar sub
|
||||||
|
getinterval proc
|
||||||
|
/startchar lastwordbreak def
|
||||||
|
/curwidth wordwidth breakwidth add def
|
||||||
|
} {
|
||||||
|
/curwidth curwidth wordwidth add
|
||||||
|
breakwidth add def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
/lastwordbreak lastwordbreak
|
||||||
|
nextword length add 1 add def
|
||||||
|
|
||||||
|
done {
|
||||||
|
exit
|
||||||
|
} {
|
||||||
|
} ifelse
|
||||||
|
} loop
|
||||||
|
/lastchar textstring length def
|
||||||
|
textstring startchar lastchar startchar sub
|
||||||
|
getinterval proc
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show some text, and word-wrap it if necessary, then move to the next line
|
||||||
|
/wshow {
|
||||||
|
0 exch
|
||||||
|
/x currentpoint pop def
|
||||||
|
rm x sub % Line length
|
||||||
|
{
|
||||||
|
show next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show indented, wrapped text
|
||||||
|
/iwshow {
|
||||||
|
indentLevel exch
|
||||||
|
/x currentpoint pop def
|
||||||
|
rm x sub
|
||||||
|
indentLevel 0 rmoveto
|
||||||
|
{
|
||||||
|
show next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show inverse-indented, wrapped text
|
||||||
|
/iiwshow {
|
||||||
|
indentLevel neg exch
|
||||||
|
/x currentpoint pop indentLevel add def
|
||||||
|
rm x sub
|
||||||
|
{
|
||||||
|
show next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
indentLevel neg 0 rmoveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
|
||||||
|
LM TM moveto
|
||||||
|
SetRegFont
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% Content goes here ;-)
|
||||||
|
|
||||||
|
(Alice in DIGITALand) cshow next
|
||||||
|
|
||||||
|
(Author: ) show (rals@pineapple.bbs.com) ishow next next align
|
||||||
|
|
||||||
|
("Where am I?" asked Alice, as she peered at the large 7-lettered sign
|
||||||
|
with the standard blue letters.) iwshow
|
||||||
|
|
||||||
|
("You're in Digitaland," replied the security guard, "May I see your
|
||||||
|
badge?") iwshow
|
||||||
|
|
||||||
|
("I don't have a badge.") iwshow
|
||||||
|
|
||||||
|
("Did you lose it?") iwshow
|
||||||
|
|
||||||
|
("No," answered Alice in a puzzled tone. "How could I lose something I
|
||||||
|
never had?") iwshow
|
||||||
|
|
||||||
|
("If it's not lost then you must show it to me.") iwshow
|
||||||
|
|
||||||
|
("I can't. I don't have one.") iwshow
|
||||||
|
|
||||||
|
("Then you'll have to have a temporary.") iwshow
|
||||||
|
|
||||||
|
("A temporary what?" asked Alice, more confused then ever.) iwshow
|
||||||
|
|
||||||
|
("A temporary Badge. What's your badge number?" requested the guard.) iwshow
|
||||||
|
|
||||||
|
("I don't have one") iwshow
|
||||||
|
|
||||||
|
("Of course not, Ken Olsen has 1. Give me your badge number, and your
|
||||||
|
cost center") iwshow
|
||||||
|
|
||||||
|
("I'm so confused. I can't do this. I've already said 3 times why. Do
|
||||||
|
I have to tell you 4?") iwshow
|
||||||
|
|
||||||
|
("Ahhh. 3XY, badge number 4. You must be very important to have such a
|
||||||
|
low badge number. I should have immediately recognized how low by your
|
||||||
|
state of extreme confusion. Here's your temporary. Go right on in.") iwshow
|
||||||
|
|
||||||
|
(Alice pasted the sticky paper to her dress and headed down the hall.
|
||||||
|
Not 10 feet ahead she saw a rather distressed looking rabbit coming
|
||||||
|
toward her. He was dressed in a pair of torn, faded jeans, and a dirty
|
||||||
|
tee shirt.) iwshow
|
||||||
|
|
||||||
|
("What's wrong?" Alice asked.) iwshow
|
||||||
|
|
||||||
|
("I'm late! I'm late!" exclaimed the rabbit as he peered at the PERT
|
||||||
|
chart dangling from his pocket protector.) iwshow
|
||||||
|
|
||||||
|
("Late for what?" asked Alice.) iwshow
|
||||||
|
|
||||||
|
("My date. I'm going to miss my date. I've got a deadline to meet and
|
||||||
|
I'm not going to make it.") iwshow
|
||||||
|
|
||||||
|
("Well, if it's already dead, it probably won't mind. In fact it isn't
|
||||||
|
likely to be going too far in such a state. I'm sure that however long
|
||||||
|
you take will be just fine.") iwshow
|
||||||
|
|
||||||
|
("You obviously don't understand. Everything takes longer than it
|
||||||
|
really does. It doesn't matter what you are doing, only that you meet
|
||||||
|
your date, and that's always impossible.") iwshow
|
||||||
|
|
||||||
|
("Well if it's impossible, why would anyone expect you to meet it?"
|
||||||
|
Almost at once regretting that she had asked. Was this was going to be
|
||||||
|
as confusing as badges?) iwshow
|
||||||
|
|
||||||
|
("It's really very simple. In order to move forward, you need a goal.
|
||||||
|
Any goal will do. It just has to be impossible to do. To motivate the
|
||||||
|
troops, you have to make goals very challenging. It's really only
|
||||||
|
there to get a stake in the ground, you know. After that we march in
|
||||||
|
step until we reach our objective. The date really doesn't mean
|
||||||
|
anything. You simple have to understand that we are going to do the
|
||||||
|
right thing.") iwshow
|
||||||
|
|
||||||
|
("But the if the goal is impossible, and really doesn't mean anything
|
||||||
|
why are you trying to go there. Wouldn't it be simpler to first figure
|
||||||
|
out what you are really going to do, then figure out how to get there?")
|
||||||
|
iwshow
|
||||||
|
|
||||||
|
("You obviously don't understand the process. And as I said before I'm
|
||||||
|
late so there is obviously only one thing to do.") iwshow
|
||||||
|
|
||||||
|
("Hurry up and rush off?" Alice asked, hoping it would sound more like
|
||||||
|
a suggestion than a question.) iwshow
|
||||||
|
|
||||||
|
("No. No. No. A meeting. Let me find the Mad Manager and a number of
|
||||||
|
involved, interested, or warm bodies.") iwshow
|
||||||
|
|
||||||
|
("That will obviously take a lot of time. I don't think you have any to
|
||||||
|
waste.) iwshow
|
||||||
|
|
||||||
|
("No it won't. All we have to do is find a conference room. There are
|
||||||
|
lots of them right over here.") iwshow
|
||||||
|
|
||||||
|
("But," started Alice, "those rooms are all full of people. Don't we
|
||||||
|
need an empty conference room?") iwshow
|
||||||
|
|
||||||
|
("Silly thought. If we want to find the Mad Manager and some meeting
|
||||||
|
attendees, why would we look in an empty conference room? Anyway, it's
|
||||||
|
impossible to ever find an empty conference room.") iwshow
|
||||||
|
|
||||||
|
(The rabbit took Alice by the hand, and promptly lead her into the
|
||||||
|
largest, fullest conference room. Alice immediately noticed that the
|
||||||
|
wastebasket was quite full of foam cups, and overhead projector bulbs.
|
||||||
|
These people had obviously been here for a long time.) iwshow
|
||||||
|
|
||||||
|
(At the head of the table sat a man with a rather funny suit wearing a
|
||||||
|
large hat.) iwshow
|
||||||
|
|
||||||
|
("Why" whispered Alice to the rabbit, "is that man wearing that funny
|
||||||
|
hat? Who is he?") iwshow
|
||||||
|
|
||||||
|
("I'm the Mad Manager," answered the man at the end of the table,
|
||||||
|
obviously overhearing the question, " And I'll be happy to tell you
|
||||||
|
why I'm wearing this Hat, but that topic is not on the agenda.") iwshow
|
||||||
|
|
||||||
|
("Why don't we change the agenda?" asked a person in the corner.) iwshow
|
||||||
|
|
||||||
|
("Is that a topic for another meeting?" replied the manager.) iwshow
|
||||||
|
|
||||||
|
("Is what a topic for another meeting?" voiced a third. "The reason
|
||||||
|
for the hat, or why we don't change the agenda?") iwshow
|
||||||
|
|
||||||
|
("Why don't we take this off line?" queried another.) iwshow
|
||||||
|
|
||||||
|
("Does everyone agree that these are all topics we should address?"
|
||||||
|
asked the mad manager.) iwshow
|
||||||
|
|
||||||
|
("Possibly so. " injected the person in the corner. "Could it be that
|
||||||
|
we have a hidden agenda?") iwshow
|
||||||
|
|
||||||
|
("Oh no!" the Mad Manager began, the dismay obvious on his face,
|
||||||
|
"someone has hidden the agenda again! Let me put on my process hat and
|
||||||
|
we'll see if we can work this issue.") iwshow
|
||||||
|
|
||||||
|
(With that, he removed his rather amusing top hat, and place a big green
|
||||||
|
fedora on his head.) iwshow
|
||||||
|
|
||||||
|
("Now, with my process hat on, I'd like to address the issue of the
|
||||||
|
hidden agenda. Since we can't have a productive meeting without an
|
||||||
|
agenda, it is up to all of us to find it.") iwshow
|
||||||
|
|
||||||
|
("But, " a voice from the corner piped in, "who is going to drive this
|
||||||
|
issue?") iwshow
|
||||||
|
|
||||||
|
("Do we have an action item here?" asked another attendee.) iwshow
|
||||||
|
|
||||||
|
("Does anyone here want to work this?" asked the mad manager.) iwshow
|
||||||
|
|
||||||
|
("Who originally brought this up?" asked another.) iwshow
|
||||||
|
|
||||||
|
("I believe that the woman who came in with the rabbit proposed this.
|
||||||
|
Shouldn't she own it?") iwshow
|
||||||
|
|
||||||
|
("Well" the Manager stated, pointing to Alice. "I'd say that this is
|
||||||
|
your issue.") iwshow
|
||||||
|
|
||||||
|
("What issue. I don't have any issues. " retorted Alice, nervously
|
||||||
|
fingering her temporary badge. "I only posed a simple question.") iwshow
|
||||||
|
|
||||||
|
("I'm not sure we can accept that," the manager declared. "We need a
|
||||||
|
date.") iwshow
|
||||||
|
|
||||||
|
("But, " Alice began, remembering what the rabbit told her about dates,
|
||||||
|
"a date is impossible.") iwshow
|
||||||
|
|
||||||
|
(From the back of the room another voice asked, "How about a date for a
|
||||||
|
date?") iwshow
|
||||||
|
|
||||||
|
("The least we can ask it that you give us a date when you will be able
|
||||||
|
to give us the date for the date." stated the person in the corner.)
|
||||||
|
iwshow
|
||||||
|
|
||||||
|
("I'm not sure I can do that," Alice opened, "since I don't know what
|
||||||
|
I'm supposed to give you a date for. I'm having a problem trying to
|
||||||
|
figure out what you want me to do.") iwshow
|
||||||
|
|
||||||
|
("We don't have any problems here, only opportunities!" Piped a chorus
|
||||||
|
of voices.) iwshow
|
||||||
|
|
||||||
|
("It's really quite obvious," the mad manager declared as he reached
|
||||||
|
behind him for a striped blue and gray beret, "let me put on my Digital
|
||||||
|
hat for a moment," he continued doffing the fedora and flipping on his
|
||||||
|
latest selection, "You must do the right thing.") iwshow
|
||||||
|
|
||||||
|
("Yes. yes. " chimed the chorus of attendees, "Do the right thing.)
|
||||||
|
iwshow
|
||||||
|
|
||||||
|
("Now, who is keeping the minutes?" the manager asked as he pitched the
|
||||||
|
beret and placed the fedora back on his head. "We need to record this
|
||||||
|
action item so we can come back to it later.") iwshow
|
||||||
|
|
||||||
|
("We obviously can't deal with this issue until we can determine whose
|
||||||
|
meeting this is?") iwshow
|
||||||
|
|
||||||
|
("Should we schedule some time to cover that topic?" asked one of the
|
||||||
|
attendees.) iwshow
|
||||||
|
|
||||||
|
("Whose going to drive this?" asked another.) iwshow
|
||||||
|
|
||||||
|
(Just at the Mad Manager was pulling out a rather worn pith helmet, a
|
||||||
|
voice in the back suggested "Let's take a break and work some of this
|
||||||
|
1x1 off line") iwshow
|
||||||
|
|
||||||
|
(Being closest to the door Alice was the first to leave. She quickly
|
||||||
|
dashed down the hall, and ran up the first flight of stairs she
|
||||||
|
encountered, relieved to be free of the madness.) iwshow
|
||||||
|
|
||||||
|
(When she opened the door the scene that confronted her made her wonder
|
||||||
|
if returning to the meeting wasn't a bad idea. Seated around a large
|
||||||
|
oval table were what appeared to be playing cards, each dressed in a
|
||||||
|
gray or navy blue three piece suit. Around each neck was a rather
|
||||||
|
oddly shaped handle \(or were they nooses?\) made of silk, or polyester.)
|
||||||
|
iwshow
|
||||||
|
|
||||||
|
("Off with her head!" screamed the queen of hearts who was sitting at
|
||||||
|
the head of the table. Alice noticed that her tie was silk, and each
|
||||||
|
card seated near her was dressed in a suit and noose combination
|
||||||
|
similar to the queen's.) iwshow
|
||||||
|
|
||||||
|
("Why would you want to remove my head?" Alice asked. By now she was
|
||||||
|
feeling beyond confused.) iwshow
|
||||||
|
|
||||||
|
("It's not a modern, iconic, user friendly, menu driven, color, PC
|
||||||
|
compatible user interface," replied the queen, in a tone that would
|
||||||
|
need to come up two notches to be vaguely considered condescending.)
|
||||||
|
iwshow
|
||||||
|
|
||||||
|
("It happens to suit me just fine," retorted Alice.) iwshow
|
||||||
|
|
||||||
|
("What are you an engineer or something?" asked the 7 of spades.) iwshow
|
||||||
|
|
||||||
|
("No, I'm Alice. Who are you?") iwshow
|
||||||
|
|
||||||
|
("Marketing." they replied in perfect fifty-two part harmony.) iwshow
|
||||||
|
|
||||||
|
("And what is that?" asked Alice.) iwshow
|
||||||
|
|
||||||
|
(There was a brief interlude of silence as each of the cards fidgeted
|
||||||
|
with their ties, checked their watches and scribbled notes on the pads
|
||||||
|
of paper contained in a handsome genuine imitation leather folder
|
||||||
|
embossed with the company logo. Then one by one, as dominoes would do,
|
||||||
|
they turned to the person on the left until they all stared at the
|
||||||
|
queen of hearts.) iwshow
|
||||||
|
|
||||||
|
(The queen cleared her throat, adjusted her tie a second time and stared
|
||||||
|
directly at Alice. "We provide the strategic thinking necessary to
|
||||||
|
grow the business.") iwshow
|
||||||
|
|
||||||
|
("Oh," said Alice, "you figure out what products to build!") iwshow
|
||||||
|
|
||||||
|
("Heavens, no!" exclaimed the Queen, "That's too tactical. We feel
|
||||||
|
it's our job to develop the vision for the long term.") iwshow
|
||||||
|
|
||||||
|
("You develop things," began Alice, "so you build the products?") iwshow
|
||||||
|
|
||||||
|
(In unison each member of the table made a face reminiscent of the look
|
||||||
|
a small child gets upon tasting spoiled dead roaches for the first
|
||||||
|
time.) iwshow
|
||||||
|
|
||||||
|
("Uggggh, that's even more tactical," jeered the chorus.) iwshow
|
||||||
|
|
||||||
|
("No! No!" shouted the Queen. "You still do not understand. We take
|
||||||
|
the pulse of the key market leaders demand curve.") iwshow
|
||||||
|
|
||||||
|
("I see now." said Alice, "You sell the products.") iwshow
|
||||||
|
|
||||||
|
(By now the chorus of cards chanting "Tac-ti-cal! Tac-ti-cal!" was
|
||||||
|
becoming too much.) iwshow
|
||||||
|
|
||||||
|
(The queen was furious and repeated her original greeting. "Off with
|
||||||
|
her head! Off With her head") iwshow
|
||||||
|
|
||||||
|
("WAIT!" demanded Alice. "I believe I understand. You are all
|
||||||
|
responsible for driving the solution opportunities for the key client
|
||||||
|
supply perceptions through strategic vision management!") iwshow
|
||||||
|
|
||||||
|
(Alice wondered if she should add something about the claws catching,
|
||||||
|
and frumious bandersnatches and thought that she'd best leave it at
|
||||||
|
that before she became ill.) iwshow
|
||||||
|
|
||||||
|
("Yes," screamed the cards, "That's exactly right!") iwshow
|
||||||
|
|
||||||
|
("And how, might I ask, do you accomplish these lofty and important
|
||||||
|
goals?") iwshow
|
||||||
|
|
||||||
|
("By calling a BOD," the queen responded.) iwshow
|
||||||
|
|
||||||
|
("And what, pray tell, might that be?" inquired Alice as she looked for
|
||||||
|
the quickest escape route, hoping that this jabber would keep her head
|
||||||
|
attached long enough to get out.) iwshow
|
||||||
|
|
||||||
|
("A Board of Directors," began the queen, just as Alice noticed the door
|
||||||
|
to the left of the table. "It's a type of high level meeting.") iwshow
|
||||||
|
|
||||||
|
("A meeting????!!!!" exclaimed Alice. "Not another meeting!" With
|
||||||
|
that she bolted for the door, no longer fearing for her head. Her only
|
||||||
|
hope was that she make it through before the agenda hit the overhead.
|
||||||
|
In a dead run, she passed through the door just as the projector lamp
|
||||||
|
flicked on. The sound of the fan was the last sound to fade as the
|
||||||
|
door closed.) iwshow
|
||||||
|
|
||||||
|
(Breathlessly she looked up to see a large open area. Directly in front
|
||||||
|
of her was an enclosed area lined on one side with triple chrome table.
|
||||||
|
A stack of plastic trays was at the foyer.) iwshow
|
||||||
|
|
||||||
|
(As she wandered through an assortment of sandwiches, prepared foods,
|
||||||
|
soft drinks and salad began their daily spiel. "Eat Me! Drink Me! Eat
|
||||||
|
Me!") iwshow
|
||||||
|
|
||||||
|
("Oh no," answered Alice, "I may know nothing about dates, and problems
|
||||||
|
and meetings and agendas, and marketing and badges, but I do know food.
|
||||||
|
I'm not gonna touch any of you. After the morning I've had I deserve a
|
||||||
|
nice cheese steak \(no lettuce\)!") iwshow
|
||||||
|
|
||||||
|
(With that, Alice opened the nearest exit door and left. A resounding
|
||||||
|
high pitched whine sang its midday good-byes as Alice returned to the
|
||||||
|
real world.) iwshow
|
||||||
|
|
||||||
|
showpage
|
|
@ -0,0 +1,111 @@
|
||||||
|
%!PS-Adobe-2.0
|
||||||
|
%%Description: Boilerplate for multi-page banners
|
||||||
|
|
||||||
|
% --- Leave this alone, go to the bottom to start writing stuff ---
|
||||||
|
|
||||||
|
% Define margins
|
||||||
|
/LM 50 def
|
||||||
|
/BM 50 def
|
||||||
|
/RM 580 LM sub def
|
||||||
|
/TM 760 BM sub def
|
||||||
|
|
||||||
|
% font types
|
||||||
|
/times {
|
||||||
|
/font /Times-Roman def
|
||||||
|
} def
|
||||||
|
|
||||||
|
/courier {
|
||||||
|
/font /Courier def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% font sizes
|
||||||
|
/monstrous {
|
||||||
|
/FontSize 312 def
|
||||||
|
/font findfont FontSize scalefont setfont
|
||||||
|
} def
|
||||||
|
|
||||||
|
/huge {
|
||||||
|
/FontSize 288 def
|
||||||
|
font findfont FontSize scalefont setfont
|
||||||
|
} def
|
||||||
|
|
||||||
|
/jumbo {
|
||||||
|
/FontSize 188 def
|
||||||
|
font findfont FontSize scalefont setfont
|
||||||
|
} def
|
||||||
|
|
||||||
|
/large {
|
||||||
|
/FontSize 122 def
|
||||||
|
font findfont FontSize scalefont setfont
|
||||||
|
} def
|
||||||
|
|
||||||
|
|
||||||
|
% Line kerning
|
||||||
|
/smooshed {
|
||||||
|
/Spacing .5 def
|
||||||
|
} def
|
||||||
|
|
||||||
|
/even {
|
||||||
|
/Spacing .75 def
|
||||||
|
} def
|
||||||
|
|
||||||
|
/wide {
|
||||||
|
/Spacing 1 def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show text in landscape
|
||||||
|
/lshow {
|
||||||
|
gsave
|
||||||
|
90 rotate
|
||||||
|
show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% start on line 1
|
||||||
|
/offset 0 def
|
||||||
|
|
||||||
|
% Form feed
|
||||||
|
/ff {
|
||||||
|
showpage
|
||||||
|
/offset 0 def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Line feed (in current font)
|
||||||
|
/lf {
|
||||||
|
/offset offset Spacing FontSize mul add def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show top text
|
||||||
|
/disp {
|
||||||
|
lf
|
||||||
|
LM offset add RM gt {
|
||||||
|
ff
|
||||||
|
lf
|
||||||
|
} {
|
||||||
|
} ifelse
|
||||||
|
LM offset add BM moveto
|
||||||
|
lshow
|
||||||
|
} def
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
%% Start writing your data here
|
||||||
|
|
||||||
|
% How do you want it to appear?
|
||||||
|
courier jumbo wide
|
||||||
|
|
||||||
|
% Even bigger font: times, letters closely spaced
|
||||||
|
%times huge smooshed
|
||||||
|
|
||||||
|
% Smaller times, normal spacing
|
||||||
|
%times large even
|
||||||
|
|
||||||
|
% Monstrous, the biggest of all
|
||||||
|
%times monstrous smooshed
|
||||||
|
|
||||||
|
(This) disp
|
||||||
|
(is some) disp
|
||||||
|
(really) disp
|
||||||
|
(large) disp
|
||||||
|
(text.) disp
|
||||||
|
ff
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,357 @@
|
||||||
|
%!PS-Adobe-2.0
|
||||||
|
%%Creator: Neale Pickett
|
||||||
|
%%Title: Emergency card
|
||||||
|
%%Pages: 2
|
||||||
|
%%PageOrder: Ascend
|
||||||
|
%%BoundingBox: 50 50 358 294
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
% A credit card is 0 0 154 244, this page is twice that so you can fold
|
||||||
|
% it in half. Store phone numbers inside or whatever.
|
||||||
|
|
||||||
|
/RegFont /Times-Roman findfont 8 scalefont def
|
||||||
|
/BoldFont /Times-Bold findfont 8 scalefont def
|
||||||
|
|
||||||
|
RegFont setfont
|
||||||
|
|
||||||
|
/bshow {
|
||||||
|
BoldFont setfont
|
||||||
|
show
|
||||||
|
RegFont setfont
|
||||||
|
} def
|
||||||
|
|
||||||
|
/h1 {
|
||||||
|
/Times-Bold findfont 11 scalefont setfont
|
||||||
|
show
|
||||||
|
RegFont setfont
|
||||||
|
} def
|
||||||
|
|
||||||
|
/nl {
|
||||||
|
currentpoint exch pop 10 exch 10 sub moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
/tab {
|
||||||
|
currentpoint exch pop moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
/hl {
|
||||||
|
gsave
|
||||||
|
currentpoint exch pop 10 exch moveto
|
||||||
|
0.3 setlinewidth
|
||||||
|
134 0 rlineto
|
||||||
|
closepath stroke
|
||||||
|
grestore
|
||||||
|
nl
|
||||||
|
} def
|
||||||
|
|
||||||
|
/star-of-life {
|
||||||
|
gsave
|
||||||
|
translate
|
||||||
|
0.13 -0.13 scale
|
||||||
|
[] 0 setdash
|
||||||
|
1 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
0 setlinecap
|
||||||
|
currentrgbcolor % push color
|
||||||
|
gsave [1 0 0 1 0 0] concat
|
||||||
|
gsave
|
||||||
|
1 1 1 setrgbcolor
|
||||||
|
newpath
|
||||||
|
71.9375 6.84375 moveto
|
||||||
|
71.9375 51.09375 lineto
|
||||||
|
33.625 28.96875 lineto
|
||||||
|
8 73.375 lineto
|
||||||
|
46.3125 95.46875 lineto
|
||||||
|
8 117.59375 lineto
|
||||||
|
33.625 162.03125 lineto
|
||||||
|
71.9375 139.90625 lineto
|
||||||
|
71.9375 184.15625 lineto
|
||||||
|
123.25 184.15625 lineto
|
||||||
|
123.25 139.9375 lineto
|
||||||
|
161.53125 162.03125 lineto
|
||||||
|
187.1875 117.59375 lineto
|
||||||
|
148.875 95.5 lineto
|
||||||
|
187.1875 73.375 lineto
|
||||||
|
161.53125 28.96875 lineto
|
||||||
|
123.25 51.0625 lineto
|
||||||
|
123.25 6.84375 lineto
|
||||||
|
71.9375 6.84375 lineto
|
||||||
|
closepath
|
||||||
|
fill
|
||||||
|
grestore
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
[] 0 setdash
|
||||||
|
1.7 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
1 setlinecap
|
||||||
|
newpath
|
||||||
|
71.9375 6.84375 moveto
|
||||||
|
71.9375 51.09375 lineto
|
||||||
|
33.625 28.96875 lineto
|
||||||
|
8 73.375 lineto
|
||||||
|
46.3125 95.46875 lineto
|
||||||
|
8 117.59375 lineto
|
||||||
|
33.625 162.03125 lineto
|
||||||
|
71.9375 139.90625 lineto
|
||||||
|
71.9375 184.15625 lineto
|
||||||
|
123.25 184.15625 lineto
|
||||||
|
123.25 139.9375 lineto
|
||||||
|
161.53125 162.03125 lineto
|
||||||
|
187.1875 117.59375 lineto
|
||||||
|
148.875 95.5 lineto
|
||||||
|
187.1875 73.375 lineto
|
||||||
|
161.53125 28.96875 lineto
|
||||||
|
123.25 51.0625 lineto
|
||||||
|
123.25 6.84375 lineto
|
||||||
|
71.9375 6.84375 lineto
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
gsave
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
newpath
|
||||||
|
75.09375 10 moveto
|
||||||
|
75.09375 56.53125 lineto
|
||||||
|
34.78125 33.25 lineto
|
||||||
|
12.28125 72.21875 lineto
|
||||||
|
52.59375 95.5 lineto
|
||||||
|
12.28125 118.75 lineto
|
||||||
|
34.78125 157.71875 lineto
|
||||||
|
75.09375 134.4375 lineto
|
||||||
|
75.09375 181 lineto
|
||||||
|
120.09375 181 lineto
|
||||||
|
120.09375 134.46875 lineto
|
||||||
|
160.375 157.71875 lineto
|
||||||
|
182.875 118.75 lineto
|
||||||
|
142.5625 95.5 lineto
|
||||||
|
182.875 72.21875 lineto
|
||||||
|
160.375 33.25 lineto
|
||||||
|
120.09375 56.5 lineto
|
||||||
|
120.09375 10 lineto
|
||||||
|
75.09375 10 lineto
|
||||||
|
closepath
|
||||||
|
fill
|
||||||
|
grestore
|
||||||
|
gsave
|
||||||
|
1 1 1 setrgbcolor
|
||||||
|
newpath
|
||||||
|
92.517151 28.749341 moveto
|
||||||
|
92.408986 25.487473 95.364102 22.444786 98.522226 22.444786 curveto
|
||||||
|
100.78169 22.444786 103.56391 24.163507 103.49852 27.028713 curveto
|
||||||
|
102.70837 61.652668 100.60467 131.89502 99.809866 166.84929 curveto
|
||||||
|
99.785836 167.9063 98.454636 167.43783 98.406332 166.35356 curveto
|
||||||
|
96.861909 131.68636 93.680464 63.830863 92.517151 28.749341 curveto
|
||||||
|
closepath
|
||||||
|
eofill
|
||||||
|
grestore
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
[] 0 setdash
|
||||||
|
0.60000002 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
0 setlinecap
|
||||||
|
newpath
|
||||||
|
92.517151 28.749341 moveto
|
||||||
|
92.408986 25.487473 95.364102 22.444786 98.522226 22.444786 curveto
|
||||||
|
100.78169 22.444786 103.56391 24.163507 103.49852 27.028713 curveto
|
||||||
|
102.70837 61.652668 100.60467 131.89502 99.809866 166.84929 curveto
|
||||||
|
99.785836 167.9063 98.454636 167.43783 98.406332 166.35356 curveto
|
||||||
|
96.861909 131.68636 93.680464 63.830863 92.517151 28.749341 curveto
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
gsave
|
||||||
|
1 1 1 setrgbcolor
|
||||||
|
newpath
|
||||||
|
94.036939 33.435356 moveto
|
||||||
|
96.542562 33.435356 97.667652 34.958914 99.217717 36.299367 curveto
|
||||||
|
100.71504 37.594206 101.88918 38.77889 101.88918 40.05277 curveto
|
||||||
|
101.88918 41.391534 100.90645 40.990382 99.865157 41.482074 curveto
|
||||||
|
99.316637 41.74108 99.365585 42.300792 98.469657 42.300792 curveto
|
||||||
|
96.316099 42.300792 95.45867 40.274406 92.51715 40.274406 curveto
|
||||||
|
90.093039 40.274406 86.944591 43.179145 86.944591 48.506596 curveto
|
||||||
|
86.944591 56.552685 90.047493 60.158311 93.720316 62.944591 curveto
|
||||||
|
94.100263 72.506596 lineto
|
||||||
|
90.174142 70.860159 80.042216 61.931473 80.042216 49.519789 curveto
|
||||||
|
80.042216 38.718116 87.055227 33.435356 94.036939 33.435356 curveto
|
||||||
|
closepath
|
||||||
|
eofill
|
||||||
|
grestore
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
[] 0 setdash
|
||||||
|
0.60000002 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
0 setlinecap
|
||||||
|
newpath
|
||||||
|
94.036939 33.435356 moveto
|
||||||
|
96.542562 33.435356 97.667652 34.958914 99.217717 36.299367 curveto
|
||||||
|
100.71504 37.594206 101.88918 38.77889 101.88918 40.05277 curveto
|
||||||
|
101.88918 41.391534 100.90645 40.990382 99.865157 41.482074 curveto
|
||||||
|
99.316637 41.74108 99.365585 42.300792 98.469657 42.300792 curveto
|
||||||
|
96.316099 42.300792 95.45867 40.274406 92.51715 40.274406 curveto
|
||||||
|
90.093039 40.274406 86.944591 43.179145 86.944591 48.506596 curveto
|
||||||
|
86.944591 56.552685 90.047493 60.158311 93.720316 62.944591 curveto
|
||||||
|
94.100263 72.506596 lineto
|
||||||
|
90.174142 70.860159 80.042216 61.931473 80.042216 49.519789 curveto
|
||||||
|
80.042216 38.718116 87.055227 33.435356 94.036939 33.435356 curveto
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
gsave
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
newpath
|
||||||
|
94.986808 36.126649 moveto
|
||||||
|
96.778096 35.842684 96.597917 36.918831 97.39314 37.614775 curveto
|
||||||
|
95.554706 37.669411 95.147378 36.898305 94.986808 36.126649 curveto
|
||||||
|
closepath
|
||||||
|
eofill
|
||||||
|
grestore
|
||||||
|
gsave
|
||||||
|
1 1 1 setrgbcolor
|
||||||
|
newpath
|
||||||
|
102.52243 66.934037 moveto
|
||||||
|
102.26913 76.05277 lineto
|
||||||
|
102.26913 81.62722 87.514512 91.407424 87.514512 101.38259 curveto
|
||||||
|
87.514512 109.06464 93.688655 113.98417 95.968338 115.12401 curveto
|
||||||
|
95.683377 107.08179 lineto
|
||||||
|
95.683377 107.08179 92.897098 105.20276 92.897098 102.01583 curveto
|
||||||
|
92.897098 97.506158 109.86807 85.891676 109.86807 77.002639 curveto
|
||||||
|
109.86807 70.699933 105.56201 68.348285 102.52243 66.934037 curveto
|
||||||
|
closepath
|
||||||
|
eofill
|
||||||
|
grestore
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
[] 0 setdash
|
||||||
|
0.60000002 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
0 setlinecap
|
||||||
|
newpath
|
||||||
|
102.52243 66.934037 moveto
|
||||||
|
102.26913 76.05277 lineto
|
||||||
|
102.26913 81.62722 87.514512 91.407424 87.514512 101.38259 curveto
|
||||||
|
87.514512 109.06464 93.688655 113.98417 95.968338 115.12401 curveto
|
||||||
|
95.683377 107.08179 lineto
|
||||||
|
95.683377 107.08179 92.897098 105.20276 92.897098 102.01583 curveto
|
||||||
|
92.897098 97.506158 109.86807 85.891676 109.86807 77.002639 curveto
|
||||||
|
109.86807 70.699933 105.56201 68.348285 102.52243 66.934037 curveto
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
gsave
|
||||||
|
1 1 1 setrgbcolor
|
||||||
|
newpath
|
||||||
|
101.35093 111.54617 moveto
|
||||||
|
101.35093 111.54617 107.14512 115.37689 107.14512 120.18997 curveto
|
||||||
|
107.14512 124.30746 103.98764 128.07412 100.25762 131.96931 curveto
|
||||||
|
95.900204 136.51968 94.980033 142.21601 97.551452 148.17942 curveto
|
||||||
|
97.773087 153.0554 lineto
|
||||||
|
95.905013 151.53561 92.960422 146.78869 92.960422 138.87071 curveto
|
||||||
|
92.960422 129.33988 101.12929 126.47258 101.12929 117.68866 curveto
|
||||||
|
101.35093 111.54617 lineto
|
||||||
|
closepath
|
||||||
|
eofill
|
||||||
|
grestore
|
||||||
|
setrgbcolor currentrgbcolor
|
||||||
|
[] 0 setdash
|
||||||
|
0.60000002 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
0 setlinecap
|
||||||
|
newpath
|
||||||
|
101.35093 111.54617 moveto
|
||||||
|
101.35093 111.54617 107.14512 115.37689 107.14512 120.18997 curveto
|
||||||
|
107.14512 124.30746 103.98764 128.07412 100.25762 131.96931 curveto
|
||||||
|
95.900204 136.51968 94.980033 142.21601 97.551452 148.17942 curveto
|
||||||
|
97.773087 153.0554 lineto
|
||||||
|
95.905013 151.53561 92.960422 146.78869 92.960422 138.87071 curveto
|
||||||
|
92.960422 129.33988 101.12929 126.47258 101.12929 117.68866 curveto
|
||||||
|
101.35093 111.54617 lineto
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
gsave
|
||||||
|
1 1 1 setrgbcolor
|
||||||
|
newpath
|
||||||
|
100.21108 151.25066 moveto
|
||||||
|
100.21108 151.25066 102.90238 152.69861 102.90238 156.47493 curveto
|
||||||
|
102.90238 160.02958 98.451601 163.27556 97.773087 163.75726 curveto
|
||||||
|
97.11579 164.2239 96.863869 163.61619 97.39314 163.12401 curveto
|
||||||
|
97.939619 162.61583 100.17942 159.61423 100.17942 156.15831 curveto
|
||||||
|
100.21108 151.25066 lineto
|
||||||
|
closepath
|
||||||
|
eofill
|
||||||
|
grestore
|
||||||
|
setrgbcolor
|
||||||
|
[] 0 setdash
|
||||||
|
0.60000002 setlinewidth
|
||||||
|
0 setlinejoin
|
||||||
|
0 setlinecap
|
||||||
|
newpath
|
||||||
|
100.21108 151.25066 moveto
|
||||||
|
100.21108 151.25066 102.90238 152.69861 102.90238 156.47493 curveto
|
||||||
|
102.90238 160.02958 98.451601 163.27556 97.773087 163.75726 curveto
|
||||||
|
97.11579 164.2239 96.863869 163.61619 97.39314 163.12401 curveto
|
||||||
|
97.939619 162.61583 100.17942 159.61423 100.17942 156.15831 curveto
|
||||||
|
100.21108 151.25066 lineto
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
grestore
|
||||||
|
|
||||||
|
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
%%Page: 1
|
||||||
|
|
||||||
|
% Get away from the edge of the paper
|
||||||
|
50 50 translate
|
||||||
|
|
||||||
|
% Draw some lines for cutting
|
||||||
|
gsave
|
||||||
|
0.9 setgray
|
||||||
|
|
||||||
|
154 0 moveto
|
||||||
|
154 244 lineto
|
||||||
|
closepath stroke
|
||||||
|
|
||||||
|
0 0 moveto
|
||||||
|
308 0 lineto
|
||||||
|
308 244 lineto
|
||||||
|
0 244 lineto
|
||||||
|
closepath stroke
|
||||||
|
grestore
|
||||||
|
|
||||||
|
125 240 star-of-life
|
||||||
|
|
||||||
|
10 230 moveto
|
||||||
|
(Name) bshow 50 tab (John Smith) show nl
|
||||||
|
(Address) bshow 50 tab (21 Jump Street) show nl
|
||||||
|
50 tab (New York, NY USA) show nl
|
||||||
|
|
||||||
|
nl
|
||||||
|
30 tab (Notify In Emergency) h1 hl
|
||||||
|
|
||||||
|
(Name) bshow 50 tab (Sally Smith) show nl
|
||||||
|
50 tab (+1 800-555-1212) show nl
|
||||||
|
(Name) bshow 50 tab (Joseph Smith) show nl
|
||||||
|
50 tab (+1 800-555-1212) show nl
|
||||||
|
(Doctor) bshow 50 tab (Mahatma Ghandi) show nl
|
||||||
|
50 tab (+1 800-555-1212) show nl
|
||||||
|
|
||||||
|
hl
|
||||||
|
|
||||||
|
(Conditions) bshow 50 tab (None) show nl
|
||||||
|
(Allergies) bshow 50 tab (None) show nl
|
||||||
|
(Meds) bshow 50 tab (None) show nl
|
||||||
|
(Blood) bshow 50 tab (A+++) show
|
||||||
|
70 tab (I'm an Organ Donor!) bshow
|
||||||
|
|
||||||
|
% Next page
|
||||||
|
154 0 translate
|
||||||
|
|
||||||
|
5 240 star-of-life
|
||||||
|
|
||||||
|
40 225 moveto
|
||||||
|
(info on other side) h1 nl
|
||||||
|
nl
|
||||||
|
nl
|
||||||
|
(You can put whatever you want here.) show nl
|
||||||
|
|
||||||
|
showpage
|
||||||
|
|
||||||
|
% Local Variables:
|
||||||
|
% mode: ps
|
||||||
|
% End:
|
|
@ -0,0 +1,32 @@
|
||||||
|
%!PS-Adobe-3.0
|
||||||
|
%%Title: A sheet of graph paper
|
||||||
|
%%Creator: Neale Pickett
|
||||||
|
%%CreationDate: Sun Oct 20 17:10:42 2002
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
64 dict begin
|
||||||
|
|
||||||
|
% How wide do you want the cells to be? (in mm)
|
||||||
|
/l 5 def
|
||||||
|
|
||||||
|
% How light should the lines be? 0.0 is black, 1.0 is white (no lines).
|
||||||
|
0.9 setgray
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% You don't need to change anything below here
|
||||||
|
|
||||||
|
% One millimeter in points
|
||||||
|
/mm 2.8452756 def
|
||||||
|
|
||||||
|
0 l mm mul 900 {
|
||||||
|
dup 0 moveto
|
||||||
|
0 900 rlineto
|
||||||
|
|
||||||
|
0 exch moveto
|
||||||
|
900 0 rlineto
|
||||||
|
} for
|
||||||
|
stroke
|
||||||
|
|
||||||
|
end
|
||||||
|
showpage
|
||||||
|
%%EOF
|
|
@ -0,0 +1,4 @@
|
||||||
|
== PostScript Hacks ==
|
||||||
|
|
||||||
|
Here are some little programs that are either too small or too
|
||||||
|
self-explanatory to deserve their own web pages.
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>PostScript Hacks
|
||||||
|
</title>
|
||||||
|
<link rel="stylesheet" title="default" type="text/css"
|
||||||
|
href="/~neale/default.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="head">
|
||||||
|
<a href="/~neale/"><img src="/~neale/logo.png" /></a>
|
||||||
|
Neale Pickett
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div id="body">
|
||||||
|
<h1>PostScript Hacks
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p>Here are some little programs that are either too small or too
|
||||||
|
self-explanatory to deserve their own web pages.</p>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt><a href=alice.ps>alice.ps</a> (19k)</dt>
|
||||||
|
<dd>Alice in DIGITALand -- an example application of skel.ps</dd>
|
||||||
|
<dt><a href=banner.ps>banner.ps</a> (2.1k)</dt>
|
||||||
|
<dd>Boilerplate for multi-page banners</dd>
|
||||||
|
<dt><a href=emergency-card.ps>emergency-card.ps</a> (11k)</dt>
|
||||||
|
<dd>Emergency card</dd>
|
||||||
|
<dt><a href=graph-paper.ps>graph-paper.ps</a> (1.1k)</dt>
|
||||||
|
<dd>A sheet of graph paper</dd>
|
||||||
|
<dt><a href=logo.ps>logo.ps</a> (1.1k)</dt>
|
||||||
|
<dd>Some functions to make PostScript feel like LOGO</dd>
|
||||||
|
<dt><a href=page_dimensions.ps>page_dimensions.ps</a> (2.1k)</dt>
|
||||||
|
<dd>Displays dimensions of the page, and where your printer clips.</dd>
|
||||||
|
<dt><a href=resume.ps>resume.ps</a> (13k)</dt>
|
||||||
|
<dd>Résumé</dd>
|
||||||
|
<dt><a href=ruled-paper.ps>ruled-paper.ps</a> (2.1k)</dt>
|
||||||
|
<dd>A sheet of ruled paper</dd>
|
||||||
|
<dt><a href=skel.ps>skel.ps</a> (5.2k)</dt>
|
||||||
|
<dd>Skeleton for typesetting in PostScript.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul id="nav">
|
||||||
|
<li><a href="/~neale/src/" title="Free Software">Software</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/~neale/src/pysieved/"
|
||||||
|
title="MANAGESIEVE server for Postfix, Exim">pysieved</a></li>
|
||||||
|
<li><a href="/~neale/src/firebot"
|
||||||
|
title="IRC bot with many features">firebot</a></li>
|
||||||
|
<li><a href="/~neale/src/ipqueue"
|
||||||
|
title="Python IPQueue library">ipqueue</a></li>
|
||||||
|
<li><a href="/~neale/toys"
|
||||||
|
title="Amusements. They amuse me anyway.">toys</a></li>
|
||||||
|
<li><a href="/~neale/repos/"
|
||||||
|
title="Things I am or was hacking">git projects</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="/~neale/tartans"
|
||||||
|
title="The woozle.org tartans database">Tartans</a></li>
|
||||||
|
<li><a href="/~neale/poems/"
|
||||||
|
title="I won't quit my day job">Poems</a></li>
|
||||||
|
<li><a href="/~neale/papers/"
|
||||||
|
title="Various writings">Papers</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/~neale/papers/reply-to-still-harmful"
|
||||||
|
title="Why your list should leave Reply-To alone">Reply-To Munging Still Considered Harmful</a></li>
|
||||||
|
<li><a href="/~neale/papers/DNS"
|
||||||
|
title="A quick introduction to DNS">How DNS Works</a></li>
|
||||||
|
<li><a href="/~neale/papers/sockets"
|
||||||
|
title="A quick introduction to TCP sockets">Introduction to TCP Sockets</a></li>
|
||||||
|
<li><a href="/blogs/neale"
|
||||||
|
title="I'm special! Look at all my angst!">Blog</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?scm (define title "PostScript Hacks") ?>
|
||||||
|
<?scm (define (body) ?>
|
||||||
|
<p>Here is some neat stuff that's too small or self-explanatory
|
||||||
|
to deserve its own web page.</p>
|
||||||
|
<dl>
|
||||||
|
<?scm (html-index-files (string-split "alice.ps banner.ps emergency-card.ps graph-paper.ps logo.ps page_dimensions.ps resume.ps ruled-paper.ps skel.ps" " ")) ?>
|
||||||
|
</dl>
|
||||||
|
<?scm ) ?>
|
|
@ -0,0 +1,34 @@
|
||||||
|
%!PS-Adobe-3.0 EPSF-3.0
|
||||||
|
%%BoundingBox: 0 0 612 792
|
||||||
|
%%Title: Some functions to make PostScript feel like LOGO
|
||||||
|
%%Creator: Neale Pickett
|
||||||
|
%%CreationDate: Tue Oct 22 09:23:22 2002
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
64 dict begin
|
||||||
|
/pen true def
|
||||||
|
/pu { /pen false def } def
|
||||||
|
/pd { /pen true def } def
|
||||||
|
/fd { /l exch def pen { 0 l rlineto } { 0 l rmoveto } ifelse } def
|
||||||
|
/rt { rotate } def
|
||||||
|
/lt { -1 mul rotate } def
|
||||||
|
/home { 306 496 moveto } def
|
||||||
|
home
|
||||||
|
|
||||||
|
%% Your LOGO-like program starts here
|
||||||
|
|
||||||
|
% a square
|
||||||
|
40 fd
|
||||||
|
90 rt
|
||||||
|
40 fd
|
||||||
|
90 rt
|
||||||
|
40 fd
|
||||||
|
90 rt
|
||||||
|
40 fd
|
||||||
|
stroke
|
||||||
|
|
||||||
|
%% Your LOGO-like program ends here
|
||||||
|
|
||||||
|
end
|
||||||
|
showpage
|
||||||
|
%%EOF
|
|
@ -0,0 +1,117 @@
|
||||||
|
%!PS-Adobe-2.0
|
||||||
|
%Description: Displays dimensions of the page, and where your printer clips.
|
||||||
|
|
||||||
|
/TM 792 def
|
||||||
|
/RM 612 def
|
||||||
|
|
||||||
|
/margin 18 def
|
||||||
|
/linelen 18 def
|
||||||
|
|
||||||
|
/step 18 def
|
||||||
|
|
||||||
|
|
||||||
|
.1 setlinewidth
|
||||||
|
/ruler {
|
||||||
|
newpath
|
||||||
|
|
||||||
|
0 0 moveto
|
||||||
|
0 1 18 {
|
||||||
|
0 6 rlineto
|
||||||
|
1 -6 rmoveto
|
||||||
|
} for
|
||||||
|
|
||||||
|
0 0 moveto
|
||||||
|
0 3 18 {
|
||||||
|
0 12 rlineto
|
||||||
|
3 -12 rmoveto
|
||||||
|
} for
|
||||||
|
|
||||||
|
9 0 moveto
|
||||||
|
0 15 rlineto
|
||||||
|
|
||||||
|
0 0 moveto
|
||||||
|
0 18 rlineto
|
||||||
|
18 0 moveto
|
||||||
|
0 18 rlineto
|
||||||
|
|
||||||
|
stroke
|
||||||
|
} def
|
||||||
|
|
||||||
|
|
||||||
|
/Times-Roman findfont 10 scalefont setfont
|
||||||
|
|
||||||
|
|
||||||
|
/str 20 string def
|
||||||
|
0 step TM {
|
||||||
|
dup dup
|
||||||
|
margin exch
|
||||||
|
gsave
|
||||||
|
translate
|
||||||
|
|
||||||
|
0 0 moveto
|
||||||
|
step 2 mul le {
|
||||||
|
pop
|
||||||
|
} {
|
||||||
|
18 0 rmoveto
|
||||||
|
str cvs str show
|
||||||
|
} ifelse
|
||||||
|
0 18 translate
|
||||||
|
-90 rotate
|
||||||
|
ruler
|
||||||
|
|
||||||
|
grestore
|
||||||
|
} for
|
||||||
|
|
||||||
|
/str 20 string def
|
||||||
|
newpath
|
||||||
|
0 step RM {
|
||||||
|
dup dup
|
||||||
|
margin
|
||||||
|
gsave
|
||||||
|
translate
|
||||||
|
0 0 moveto
|
||||||
|
|
||||||
|
step 2 mul le {
|
||||||
|
pop
|
||||||
|
} {
|
||||||
|
0 18 rmoveto
|
||||||
|
str cvs str show
|
||||||
|
} ifelse
|
||||||
|
ruler
|
||||||
|
grestore
|
||||||
|
} for
|
||||||
|
|
||||||
|
1 setlinewidth
|
||||||
|
|
||||||
|
% Score 1" in on all sides
|
||||||
|
newpath
|
||||||
|
|
||||||
|
72 0 moveto
|
||||||
|
0 36 rlineto
|
||||||
|
|
||||||
|
72 TM moveto
|
||||||
|
0 -36 rlineto
|
||||||
|
|
||||||
|
RM 72 sub 0 moveto
|
||||||
|
0 36 rlineto
|
||||||
|
|
||||||
|
RM 72 sub TM moveto
|
||||||
|
0 -36 rlineto
|
||||||
|
|
||||||
|
|
||||||
|
0 72 moveto
|
||||||
|
36 0 rlineto
|
||||||
|
|
||||||
|
RM 72 moveto
|
||||||
|
-36 0 rlineto
|
||||||
|
|
||||||
|
0 TM 72 sub moveto
|
||||||
|
36 0 rlineto
|
||||||
|
|
||||||
|
RM TM 72 sub moveto
|
||||||
|
-37 0 rlineto
|
||||||
|
|
||||||
|
stroke
|
||||||
|
|
||||||
|
|
||||||
|
showpage
|
|
@ -0,0 +1,559 @@
|
||||||
|
%!PS-Adobe-2.0
|
||||||
|
%%Title: Résumé
|
||||||
|
%%Creator: Neale Pickett <neale@lanl.gov>
|
||||||
|
%%CreationDate: Thu Nov 22 15:27:53 MST 1998
|
||||||
|
%% Time-stamp: <2007-10-15 22:16:40 neale>
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
%%
|
||||||
|
%%
|
||||||
|
%%
|
||||||
|
%% If you are reading this, I really want to work for you :-)
|
||||||
|
%%
|
||||||
|
%% Seriously.
|
||||||
|
%%
|
||||||
|
%%
|
||||||
|
%%
|
||||||
|
|
||||||
|
% You know, this was kinda fun. I'd never really used a stack-based
|
||||||
|
% language before, except for programming my HP calculator.
|
||||||
|
%
|
||||||
|
% Feel free to do with this as you please, but it comes with ABSOLUTELY
|
||||||
|
% NO WARRANTY, express or implied, including merchantability or fitness
|
||||||
|
% for a particular purpose. To quote man chat(1): "If it breaks, you
|
||||||
|
% get to keep both pieces."
|
||||||
|
|
||||||
|
[/linux /database /documentation /sysadmin /windows /math /humor] {
|
||||||
|
false def
|
||||||
|
} forall
|
||||||
|
|
||||||
|
/position (System Administrator or Database Engineer in a fast-paced\
|
||||||
|
and challenging environment) def
|
||||||
|
%/linux true def
|
||||||
|
/database true def
|
||||||
|
%/documentation true def
|
||||||
|
/sysadmin true def
|
||||||
|
%/windows true def
|
||||||
|
%/math true def
|
||||||
|
/humor true def
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% Some definitions. Season to taste.
|
||||||
|
|
||||||
|
/FontSize 12 def
|
||||||
|
|
||||||
|
/RegFont /Times-Roman def
|
||||||
|
/BoldFont /Times-Bold def
|
||||||
|
/ItalFont /Times-Italic def
|
||||||
|
|
||||||
|
/RegFSet RegFont findfont FontSize scalefont def
|
||||||
|
/BoldFSet BoldFont findfont FontSize scalefont def
|
||||||
|
/HeadFSet BoldFont findfont FontSize 1.1 mul scalefont def
|
||||||
|
/ItalFSet ItalFont findfont FontSize scalefont def
|
||||||
|
|
||||||
|
/SetRegFont { RegFSet setfont } def
|
||||||
|
/SetBoldFont { BoldFSet setfont } def
|
||||||
|
/SetHeadFont { HeadFSet setfont } def
|
||||||
|
/SetItalFont { ItalFSet setfont } def
|
||||||
|
|
||||||
|
/LM 50 def
|
||||||
|
/BM 50 def
|
||||||
|
/RM 580 LM sub def
|
||||||
|
/TM 760 BM sub def
|
||||||
|
|
||||||
|
/indentLevel 30 def
|
||||||
|
|
||||||
|
% A little buffer around left and right-justified text
|
||||||
|
SetRegFont
|
||||||
|
(M) stringwidth pop
|
||||||
|
/padwidth exch def
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% Some handy operations.
|
||||||
|
|
||||||
|
% concatenate two strings
|
||||||
|
/strcat {
|
||||||
|
/s2 exch def
|
||||||
|
/s1 exch def
|
||||||
|
|
||||||
|
s1 length s2 length add
|
||||||
|
string /s exch def
|
||||||
|
s 0 s1 putinterval
|
||||||
|
s s1 length s2 putinterval
|
||||||
|
s
|
||||||
|
} def
|
||||||
|
|
||||||
|
/lm LM def
|
||||||
|
/rm RM def
|
||||||
|
|
||||||
|
% set the tab stop
|
||||||
|
/settab {
|
||||||
|
stringwidth pop
|
||||||
|
currentpoint pop add
|
||||||
|
/tabstop exch def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% tab out there
|
||||||
|
/tab {
|
||||||
|
tabstop currentpoint exch pop moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% The TeX logo thingy
|
||||||
|
/TeX {
|
||||||
|
% Times Roman has smaller serifs and shallower angles than Computer
|
||||||
|
% Modern Roman, so I had to be creative about how things line up. I
|
||||||
|
% think I did mostly okay, and I hope you do too.
|
||||||
|
/Times-Roman findfont FontSize scalefont setfont
|
||||||
|
(T) show
|
||||||
|
FontSize 8 div neg FontSize 6.666666666666 div neg rmoveto
|
||||||
|
(E) show
|
||||||
|
FontSize 15 div neg FontSize 6.666666666666 div rmoveto
|
||||||
|
(X) show
|
||||||
|
} def
|
||||||
|
|
||||||
|
% The LaTeX logo thingy
|
||||||
|
/LaTeX {
|
||||||
|
/Times-Roman findfont dup FontSize scalefont setfont
|
||||||
|
(L) show
|
||||||
|
gsave
|
||||||
|
FontSize 1.25 div scalefont setfont
|
||||||
|
FontSize 3.428571428571 div neg FontSize 6 div rmoveto
|
||||||
|
(A) show
|
||||||
|
grestore
|
||||||
|
FontSize 4.8 div 0 rmoveto
|
||||||
|
TeX
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Bullet-list an item
|
||||||
|
/bullet {
|
||||||
|
gsave
|
||||||
|
indentLevel 3 div neg 0 rmoveto
|
||||||
|
SetRegFont
|
||||||
|
(\267) show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% In theory, all this lm, LM, rm, RM stuff should make it so that if
|
||||||
|
%% you right- or left-justify something, the word wrapper will be smart
|
||||||
|
%% enough to wrap around it on that line. In practice, this doesn't
|
||||||
|
%% work. But I've left the code in that was supposed to do it, in case
|
||||||
|
%% I get bored some day.
|
||||||
|
%%
|
||||||
|
|
||||||
|
% Re-set the margins
|
||||||
|
/reset_margins {
|
||||||
|
/lm LM def
|
||||||
|
/rm RM def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move down just a little
|
||||||
|
/down {
|
||||||
|
reset_margins
|
||||||
|
lm indentation add currentpoint exch pop
|
||||||
|
FontSize 3 div sub
|
||||||
|
moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move to the next line
|
||||||
|
/next {
|
||||||
|
reset_margins
|
||||||
|
lm indentation add currentpoint exch pop % LM Y
|
||||||
|
FontSize 1.1 mul sub % LM Y'
|
||||||
|
moveto
|
||||||
|
currentpoint exch pop
|
||||||
|
BM lt {
|
||||||
|
showpage
|
||||||
|
TM LM moveto
|
||||||
|
} {} ifelse
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move to the previous line
|
||||||
|
/prev {
|
||||||
|
lm indentation add currentpoint exch pop % LM Y
|
||||||
|
FontSize 1.1 mul add % LM Y'
|
||||||
|
moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Re-align the indentation
|
||||||
|
/align {
|
||||||
|
lm indentation add currentpoint exch pop moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Indent once
|
||||||
|
/indentation 0 def
|
||||||
|
/indent {
|
||||||
|
/indentation indentation indentLevel add def
|
||||||
|
align
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Deindent
|
||||||
|
/deindent {
|
||||||
|
/indentation indentation indentLevel sub def
|
||||||
|
align
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show left justified
|
||||||
|
/lshow {
|
||||||
|
gsave
|
||||||
|
% Set the left margin
|
||||||
|
dup
|
||||||
|
stringwidth pop
|
||||||
|
LM add
|
||||||
|
/lm exch def
|
||||||
|
|
||||||
|
currentpoint exch pop % (str) Y
|
||||||
|
LM exch % (str) x Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show centered
|
||||||
|
/cshow {
|
||||||
|
gsave
|
||||||
|
dup % (str) (str)
|
||||||
|
stringwidth pop % (str) x
|
||||||
|
2 div % (str) x/2
|
||||||
|
RM LM sub 2 div % (str) x/2 RM/2
|
||||||
|
exch sub LM add % (str) x'
|
||||||
|
currentpoint exch pop % (str) x' Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show right justified
|
||||||
|
/rshow {
|
||||||
|
gsave
|
||||||
|
dup % (str) (str)
|
||||||
|
stringwidth pop % (str) x
|
||||||
|
|
||||||
|
% set the right margin
|
||||||
|
dup
|
||||||
|
RM exch sub padwidth sub
|
||||||
|
/rm exch def
|
||||||
|
|
||||||
|
RM exch sub % (str) x'
|
||||||
|
currentpoint exch pop % (str) x' Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show in a bold font
|
||||||
|
/bshow {
|
||||||
|
SetBoldFont
|
||||||
|
show
|
||||||
|
SetRegFont
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show in a italics font
|
||||||
|
/ishow {
|
||||||
|
SetItalFont
|
||||||
|
show
|
||||||
|
SetRegFont
|
||||||
|
} def
|
||||||
|
|
||||||
|
% I totally stole this out of the blue book.
|
||||||
|
/wordbreak ( ) def
|
||||||
|
/BreakIntoLines {
|
||||||
|
/proc exch def
|
||||||
|
/linelength exch def
|
||||||
|
/textstring exch def
|
||||||
|
|
||||||
|
/breakwidth wordbreak stringwidth pop def
|
||||||
|
/curwidth 0 def
|
||||||
|
/lastwordbreak 0 def
|
||||||
|
|
||||||
|
/startchar 0 def
|
||||||
|
/restoftext textstring def
|
||||||
|
|
||||||
|
{
|
||||||
|
restoftext wordbreak search
|
||||||
|
|
||||||
|
{
|
||||||
|
/done false def
|
||||||
|
} {
|
||||||
|
() exch
|
||||||
|
wordbreak exch
|
||||||
|
/done true def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
/nextword exch def pop
|
||||||
|
/restoftext exch def
|
||||||
|
/wordwidth nextword stringwidth pop def
|
||||||
|
|
||||||
|
curwidth wordwidth add linelength gt
|
||||||
|
{
|
||||||
|
textstring startchar
|
||||||
|
lastwordbreak startchar sub
|
||||||
|
getinterval proc
|
||||||
|
/startchar lastwordbreak def
|
||||||
|
/curwidth wordwidth breakwidth add def
|
||||||
|
} {
|
||||||
|
/curwidth curwidth wordwidth add
|
||||||
|
breakwidth add def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
/lastwordbreak lastwordbreak
|
||||||
|
nextword length add 1 add def
|
||||||
|
|
||||||
|
done {
|
||||||
|
exit
|
||||||
|
} {
|
||||||
|
} ifelse
|
||||||
|
} loop
|
||||||
|
/lastchar textstring length def
|
||||||
|
textstring startchar lastchar startchar sub
|
||||||
|
getinterval proc
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show some text, and word-wrap it if necessary, then move to the next line
|
||||||
|
/wshow {
|
||||||
|
/x currentpoint pop def
|
||||||
|
rm x sub % Line length
|
||||||
|
{
|
||||||
|
show next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show as a heading (larger font, and move to next line)
|
||||||
|
/hshow {
|
||||||
|
FontSize exch
|
||||||
|
/FontSize FontSize 1.1 mul def
|
||||||
|
gsave
|
||||||
|
BoldFont findfont FontSize scalefont setfont
|
||||||
|
show
|
||||||
|
grestore
|
||||||
|
next
|
||||||
|
/FontSize exch def
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Draw a horizontal line from margin to margin
|
||||||
|
/hline {
|
||||||
|
gsave
|
||||||
|
LM currentpoint exch pop 1 sub moveto
|
||||||
|
1 setlinewidth
|
||||||
|
RM LM sub 0 rlineto
|
||||||
|
stroke
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% The actual text.
|
||||||
|
|
||||||
|
|
||||||
|
newpath
|
||||||
|
|
||||||
|
|
||||||
|
0 TM moveto
|
||||||
|
SetHeadFont
|
||||||
|
(Neale Pickett) cshow next
|
||||||
|
SetRegFont
|
||||||
|
(neale@pobox.com) cshow next
|
||||||
|
(134 Long View Dr. #11) cshow next
|
||||||
|
(White Rock, NM 87544) cshow next
|
||||||
|
(\(505\) 665-3740) cshow next
|
||||||
|
(http://people.lanl.gov/neale) cshow next
|
||||||
|
|
||||||
|
next
|
||||||
|
next
|
||||||
|
|
||||||
|
(Desired Position: ) hshow
|
||||||
|
indent position wshow deindent
|
||||||
|
|
||||||
|
next
|
||||||
|
|
||||||
|
|
||||||
|
(Work History:) hshow
|
||||||
|
|
||||||
|
|
||||||
|
indent
|
||||||
|
(1997-Present ) bshow
|
||||||
|
(Technical Staff Member: Network Engineering Group, LANL) wshow
|
||||||
|
indent
|
||||||
|
bullet (Designed and implemented a high-availability, encrypted\
|
||||||
|
database proxy server capable of handling over 45,000 transactions\
|
||||||
|
per day at rates exceeding 4 per second) wshow
|
||||||
|
bullet (Specified, created, and administered 18,000-record LDAP\
|
||||||
|
directory server, web phonebook, and mission-critical locator\
|
||||||
|
client) wshow
|
||||||
|
bullet (Designed and created web \(CGI\) programming tools, now in use\
|
||||||
|
by three major lab-wide applications, each of which service over\
|
||||||
|
20,000 users) wshow
|
||||||
|
bullet (Created Linux device driver for 1.28+1.28 Gbit/sec networking\
|
||||||
|
project) wshow
|
||||||
|
deindent
|
||||||
|
|
||||||
|
down
|
||||||
|
|
||||||
|
(1996-1997 ) bshow
|
||||||
|
(Lab Assistant / Grader, New Mexico Tech) wshow
|
||||||
|
|
||||||
|
down
|
||||||
|
|
||||||
|
(1994-1996 ) bshow
|
||||||
|
(User Consultant and Systems Programmer, New Mexico Tech) wshow
|
||||||
|
indent
|
||||||
|
bullet (Designed, created, and supported 20-machine campus Macintosh\
|
||||||
|
lab and associated configuration integrity package) wshow
|
||||||
|
bullet (Maintained and assisted installation of 160+ machine SunOS and\
|
||||||
|
Linux network, including all campus-wide servers) wshow
|
||||||
|
deindent
|
||||||
|
deindent
|
||||||
|
|
||||||
|
windows {
|
||||||
|
down
|
||||||
|
|
||||||
|
(Summer, 1992 ) bshow
|
||||||
|
(Student Aid/GS, NMERI, Albuquerque, NM) wshow
|
||||||
|
indent
|
||||||
|
bullet (Created custom application to link output from a DOS program\
|
||||||
|
to any Windows program, using DDE) wshow
|
||||||
|
deindent
|
||||||
|
} {} ifelse
|
||||||
|
|
||||||
|
next
|
||||||
|
|
||||||
|
linux {
|
||||||
|
(Linux Experience:) hshow
|
||||||
|
|
||||||
|
indent
|
||||||
|
bullet (4 years Linux administration and programming experience) wshow
|
||||||
|
bullet (6 months experience coding Linux device drivers and kernel\
|
||||||
|
hacking) wshow
|
||||||
|
bullet (Debian package maintainer \(pending 2.1 release\)) wshow
|
||||||
|
bullet (Installed and maintain 2.0.x Linux on Intel, Alpha, \
|
||||||
|
M68k, and PPC machines) wshow
|
||||||
|
humor {
|
||||||
|
bullet (Can create an XF86Config by hand) show
|
||||||
|
/Courier findfont FontSize scalefont setfont
|
||||||
|
0 FontSize 14 div rmoveto
|
||||||
|
( ;) show
|
||||||
|
0 FontSize 14 div neg rmoveto
|
||||||
|
FontSize 6 div neg 0 rmoveto
|
||||||
|
(-) show
|
||||||
|
FontSize 6 div neg 0 rmoveto
|
||||||
|
(\)) show
|
||||||
|
SetRegFont
|
||||||
|
next
|
||||||
|
} {} ifelse
|
||||||
|
deindent
|
||||||
|
|
||||||
|
next
|
||||||
|
} {} ifelse
|
||||||
|
|
||||||
|
(Authored GPL Software:) hshow
|
||||||
|
|
||||||
|
indent
|
||||||
|
SetBoldFont (phonebook ) settab
|
||||||
|
|
||||||
|
(whiz) bshow
|
||||||
|
(\(Python\)) rshow
|
||||||
|
tab (Sequential CGI forms tool \(Presented at 7th annual International Python Conference\)) wshow
|
||||||
|
align
|
||||||
|
|
||||||
|
(SQLd) bshow
|
||||||
|
(\(C\)) rshow
|
||||||
|
tab (Lightweight, encrypted, database proxy) wshow
|
||||||
|
align
|
||||||
|
|
||||||
|
(fmsh) bshow
|
||||||
|
(\(C\)) rshow
|
||||||
|
tab (Restricted execution shell \(used in LANL's lab-wide firewall\
|
||||||
|
proxy\)) wshow
|
||||||
|
align
|
||||||
|
|
||||||
|
(phonebook) bshow
|
||||||
|
(\(Python\)) rshow
|
||||||
|
tab (Web-based phonebook view of LDAP data) wshow
|
||||||
|
align
|
||||||
|
deindent
|
||||||
|
|
||||||
|
next
|
||||||
|
|
||||||
|
(Brief Knowledge List:) hshow
|
||||||
|
|
||||||
|
indent
|
||||||
|
|
||||||
|
/other () def
|
||||||
|
|
||||||
|
linux {
|
||||||
|
SetBoldFont (Server Software ) settab
|
||||||
|
/other other (Unix, Windows, MacOS, ) strcat def
|
||||||
|
} {
|
||||||
|
SetBoldFont (Operating Systems ) settab
|
||||||
|
(Operating Systems) bshow
|
||||||
|
tab
|
||||||
|
(Linux \(Red Hat and Debian\) expert, Solaris, Windows, MacOS) wshow align
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
(Languages) bshow
|
||||||
|
tab
|
||||||
|
(Python, C/C++, Perl, sh, Tcl, PostScript, ) show TeX (, etc.) show next
|
||||||
|
|
||||||
|
documentation {
|
||||||
|
(Documentation) bshow
|
||||||
|
tab
|
||||||
|
(HTML, DocBook, LinuxDoc, ) show LaTeX next
|
||||||
|
} {
|
||||||
|
/other other (documentation tools \(HTML, SGML\), ) strcat def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
database {
|
||||||
|
(Databases) bshow
|
||||||
|
tab
|
||||||
|
(SQL, Sybase, Postgres, OpenDB library) wshow align
|
||||||
|
} {
|
||||||
|
/other other (databases, ) strcat def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
sysadmin {
|
||||||
|
(Server Software) bshow
|
||||||
|
tab
|
||||||
|
(Apache, UMich LDAP, ssh, Sendmail, inn) wshow align
|
||||||
|
|
||||||
|
(Unix Utilities) bshow
|
||||||
|
tab
|
||||||
|
(GNU Shellutils, Emacs, vi, awk, sed, etc.) wshow align
|
||||||
|
|
||||||
|
(Networking) bshow
|
||||||
|
tab
|
||||||
|
(Firewalls, proxies, routing, troubleshooting) wshow align
|
||||||
|
} {
|
||||||
|
/other other (servers \(email, LDAP, web, etc.\), Unix tools,\
|
||||||
|
networking, ) strcat def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
other () eq {} {
|
||||||
|
(Other) bshow
|
||||||
|
tab
|
||||||
|
other (etc.) strcat wshow
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
deindent
|
||||||
|
|
||||||
|
next
|
||||||
|
|
||||||
|
(Education:) hshow
|
||||||
|
|
||||||
|
indent
|
||||||
|
(BS Comp. Sci. ) bshow
|
||||||
|
(New Mexico Tech \(1997, ) show (cum laude) ishow (\)) show
|
||||||
|
math {
|
||||||
|
indent
|
||||||
|
(Emphasis in mathematics/music) wshow
|
||||||
|
deindent
|
||||||
|
|
||||||
|
(Math/Music studies ) bshow
|
||||||
|
(Texas Tech \(1992-1993\)) wshow
|
||||||
|
} {} ifelse
|
||||||
|
deindent
|
||||||
|
|
||||||
|
showpage
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
%!PS-Adobe-3.0
|
||||||
|
%%Title: A sheet of ruled paper
|
||||||
|
%%Creator: Neale Pickett
|
||||||
|
%%CreationDate: Sun Oct 20 17:10:42 2002
|
||||||
|
%%Pages: 1
|
||||||
|
%%BoundingBox: 0 0 612 792
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
|
||||||
|
% Body height (in mm)
|
||||||
|
% 9 - big chief
|
||||||
|
% 5 - wide
|
||||||
|
% 4 - average
|
||||||
|
% 3 - teensy
|
||||||
|
/bh 9 def
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% You don't need to change anything below here
|
||||||
|
|
||||||
|
|
||||||
|
% How much additional space between lines (mm)?
|
||||||
|
/ls 0 def
|
||||||
|
|
||||||
|
% Show waist line?
|
||||||
|
/wl true def
|
||||||
|
|
||||||
|
% Show ascenders and descenders?
|
||||||
|
/ad false def
|
||||||
|
|
||||||
|
9 bh le {
|
||||||
|
/ls 4 def
|
||||||
|
/wl true def
|
||||||
|
/ad true def
|
||||||
|
} if
|
||||||
|
|
||||||
|
5 bh eq {
|
||||||
|
/ls 2 def
|
||||||
|
} if
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% You really don't need to change anything below here
|
||||||
|
|
||||||
|
% One millimeter in points
|
||||||
|
/mm 2.8452756 def
|
||||||
|
|
||||||
|
% Distance from baseline to baseline, in points
|
||||||
|
/lp bh 2 mul ls add mm mul def
|
||||||
|
|
||||||
|
% Body height, in points
|
||||||
|
/bp bh mm mul def
|
||||||
|
|
||||||
|
792 lp sub lp neg 0 {
|
||||||
|
20 exch moveto
|
||||||
|
|
||||||
|
ad {
|
||||||
|
% ascender and descender
|
||||||
|
gsave
|
||||||
|
0.9 setgray
|
||||||
|
0 bp 2 div rmoveto
|
||||||
|
900 0 rlineto
|
||||||
|
-900 bp rmoveto
|
||||||
|
900 0 rlineto
|
||||||
|
stroke
|
||||||
|
grestore
|
||||||
|
} if
|
||||||
|
|
||||||
|
% waist line
|
||||||
|
wl {
|
||||||
|
gsave
|
||||||
|
0.7 setgray
|
||||||
|
0 bp rmoveto
|
||||||
|
900 0 rlineto
|
||||||
|
stroke
|
||||||
|
grestore
|
||||||
|
} if
|
||||||
|
|
||||||
|
% left margin
|
||||||
|
gsave
|
||||||
|
0.3 setgray
|
||||||
|
0 bp rlineto
|
||||||
|
stroke
|
||||||
|
grestore
|
||||||
|
|
||||||
|
% base line
|
||||||
|
gsave
|
||||||
|
0.3 setgray
|
||||||
|
900 0 rlineto
|
||||||
|
stroke
|
||||||
|
grestore
|
||||||
|
} for
|
||||||
|
|
||||||
|
showpage
|
||||||
|
%%EOF
|
|
@ -0,0 +1,273 @@
|
||||||
|
%!PS-Adobe-2.0
|
||||||
|
%%Title: Skeleton for typesetting in PostScript.
|
||||||
|
%%Creator: Neale Pickett <neale@lanl.gov>
|
||||||
|
%%CreationDate: Thu Nov 22 15:27:53 MST 1998
|
||||||
|
%% Time-stamp: <2002-10-22 09:34:13 neale>
|
||||||
|
%%EndComments
|
||||||
|
|
||||||
|
/FontSize 12 def
|
||||||
|
|
||||||
|
/RegFont /Times-Roman def
|
||||||
|
/BoldFont /Times-Bold def
|
||||||
|
/ItalFont /Times-Italic def
|
||||||
|
|
||||||
|
/RegFSet RegFont findfont FontSize scalefont def
|
||||||
|
/BoldFSet BoldFont findfont FontSize scalefont def
|
||||||
|
/HeadFSet BoldFont findfont FontSize 1.1 mul scalefont def
|
||||||
|
/ItalFSet ItalFont findfont FontSize scalefont def
|
||||||
|
|
||||||
|
/SetRegFont { RegFSet setfont } def
|
||||||
|
/SetBoldFont { BoldFSet setfont } def
|
||||||
|
/SetHeadFont { HeadFSet setfont } def
|
||||||
|
/SetItalFont { ItalFSet setfont } def
|
||||||
|
|
||||||
|
/LM 50 def
|
||||||
|
/BM 50 def
|
||||||
|
/RM 580 LM sub def
|
||||||
|
/TM 760 BM sub def
|
||||||
|
|
||||||
|
/indentLevel 30 def
|
||||||
|
|
||||||
|
% Re-set the margins
|
||||||
|
/reset_margins {
|
||||||
|
/lm LM def
|
||||||
|
/rm RM def
|
||||||
|
} def
|
||||||
|
|
||||||
|
reset_margins
|
||||||
|
|
||||||
|
% Move down just a little
|
||||||
|
/down {
|
||||||
|
reset_margins
|
||||||
|
lm indentation add currentpoint exch pop
|
||||||
|
FontSize 3 div sub
|
||||||
|
moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move to the next line
|
||||||
|
/next {
|
||||||
|
reset_margins
|
||||||
|
lm indentation add currentpoint exch pop % LM Y
|
||||||
|
FontSize 1.1 mul sub % LM Y'
|
||||||
|
moveto
|
||||||
|
currentpoint exch pop
|
||||||
|
BM lt {
|
||||||
|
showpage
|
||||||
|
LM TM moveto
|
||||||
|
} {} ifelse
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Move to the previous line
|
||||||
|
/prev {
|
||||||
|
lm indentation add currentpoint exch pop % LM Y
|
||||||
|
FontSize 1.1 mul add % LM Y'
|
||||||
|
moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Re-align the indentation
|
||||||
|
/align {
|
||||||
|
lm indentation add currentpoint exch pop moveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Indent once
|
||||||
|
/indentation 0 def
|
||||||
|
/indent {
|
||||||
|
/indentation indentation indentLevel add def
|
||||||
|
align
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Deindent
|
||||||
|
/deindent {
|
||||||
|
/indentation indentation indentLevel sub def
|
||||||
|
align
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show left justified
|
||||||
|
/lshow {
|
||||||
|
gsave
|
||||||
|
% Set the left margin
|
||||||
|
dup
|
||||||
|
stringwidth pop
|
||||||
|
LM add
|
||||||
|
/lm exch def
|
||||||
|
|
||||||
|
currentpoint exch pop % (str) Y
|
||||||
|
LM exch % (str) x Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show centered
|
||||||
|
/cshow {
|
||||||
|
gsave
|
||||||
|
dup % (str) (str)
|
||||||
|
stringwidth pop % (str) x
|
||||||
|
2 div % (str) x/2
|
||||||
|
RM LM sub 2 div % (str) x/2 RM/2
|
||||||
|
exch sub LM add % (str) x'
|
||||||
|
currentpoint exch pop % (str) x' Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show right justified
|
||||||
|
/rshow {
|
||||||
|
gsave
|
||||||
|
dup % (str) (str)
|
||||||
|
stringwidth pop % (str) x
|
||||||
|
|
||||||
|
% set the right margin
|
||||||
|
dup
|
||||||
|
RM exch sub padwidth sub
|
||||||
|
/rm exch def
|
||||||
|
|
||||||
|
RM exch sub % (str) x'
|
||||||
|
currentpoint exch pop % (str) x' Y
|
||||||
|
moveto show
|
||||||
|
grestore
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show in a bold font
|
||||||
|
/bshow {
|
||||||
|
SetBoldFont
|
||||||
|
show
|
||||||
|
SetRegFont
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show in a italics font
|
||||||
|
/ishow {
|
||||||
|
SetItalFont
|
||||||
|
show
|
||||||
|
SetRegFont
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Special parsing stuff; returns true if it's a printing character
|
||||||
|
/parse_special {
|
||||||
|
/word exch def
|
||||||
|
(<b>) word eq {
|
||||||
|
SetBoldFont
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
(<i>) word eq {
|
||||||
|
SetItalFont
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
(<r>) word eq {
|
||||||
|
SetRegFont
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
true
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
|
||||||
|
% I totally stole this out of the blue book. But I changed it a lot, so
|
||||||
|
% you might not recognize it if not for the name.
|
||||||
|
/wordbreak ( ) def
|
||||||
|
/BreakIntoLines {
|
||||||
|
/proc exch def
|
||||||
|
/linelength exch def
|
||||||
|
/textstring exch def
|
||||||
|
/curwidth exch def
|
||||||
|
|
||||||
|
/breakwidth wordbreak stringwidth pop def
|
||||||
|
/lastwordbreak 0 def
|
||||||
|
|
||||||
|
/startchar 0 def
|
||||||
|
/restoftext textstring def
|
||||||
|
|
||||||
|
SetRegFont
|
||||||
|
{
|
||||||
|
restoftext wordbreak search
|
||||||
|
|
||||||
|
{
|
||||||
|
/done false def
|
||||||
|
} {
|
||||||
|
() exch
|
||||||
|
wordbreak exch
|
||||||
|
/done true def
|
||||||
|
} ifelse
|
||||||
|
|
||||||
|
/nextword exch def pop
|
||||||
|
/restoftext exch def
|
||||||
|
|
||||||
|
nextword parse_special {
|
||||||
|
/wordwidth nextword stringwidth pop def
|
||||||
|
curwidth wordwidth add linelength gt
|
||||||
|
{
|
||||||
|
% This word should go on the next line
|
||||||
|
proc
|
||||||
|
/curwidth exch def
|
||||||
|
} if
|
||||||
|
|
||||||
|
/curwidth curwidth wordwidth add
|
||||||
|
breakwidth add def
|
||||||
|
nextword show
|
||||||
|
wordbreak show
|
||||||
|
|
||||||
|
/lastwordbreak lastwordbreak
|
||||||
|
nextword length add 1 add def
|
||||||
|
|
||||||
|
done {
|
||||||
|
exit
|
||||||
|
} {
|
||||||
|
} ifelse
|
||||||
|
} if
|
||||||
|
} loop
|
||||||
|
/lastchar textstring length def
|
||||||
|
textstring startchar lastchar startchar sub
|
||||||
|
getinterval proc
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show some text, and word-wrap it if necessary, then move to the next line
|
||||||
|
/wshow {
|
||||||
|
0 exch
|
||||||
|
/x currentpoint pop def
|
||||||
|
rm x sub % Line length
|
||||||
|
{
|
||||||
|
next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
0
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show indented, wrapped text
|
||||||
|
/iwshow {
|
||||||
|
indentLevel exch
|
||||||
|
/x currentpoint pop def
|
||||||
|
rm x sub
|
||||||
|
indentLevel 0 rmoveto
|
||||||
|
{
|
||||||
|
next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
0
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
} def
|
||||||
|
|
||||||
|
% Show inverse-indented, wrapped text
|
||||||
|
/iiwshow {
|
||||||
|
indentLevel neg exch
|
||||||
|
/x currentpoint pop indentLevel add def
|
||||||
|
rm x sub
|
||||||
|
{
|
||||||
|
next
|
||||||
|
x currentpoint exch pop moveto
|
||||||
|
0
|
||||||
|
}
|
||||||
|
BreakIntoLines
|
||||||
|
indentLevel neg 0 rmoveto
|
||||||
|
} def
|
||||||
|
|
||||||
|
|
||||||
|
LM TM moveto
|
||||||
|
SetRegFont
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
% Content goes here ;-)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
showpage
|
|
@ -0,0 +1,3 @@
|
||||||
|
[[!meta title="Python hacks"]]
|
||||||
|
|
||||||
|
[[!map pages="src/python/*" show="description"]]
|
|
@ -0,0 +1,100 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
## htmlpp -- Python HTML/CSS pretty-printer
|
||||||
|
##
|
||||||
|
## Thanks to ActiveState's ASPN (which rules, thanks guys) for leading
|
||||||
|
## me to this idea. AFAIK, they got the idea from MoinMoin.
|
||||||
|
##
|
||||||
|
## This is a part of epy: http://woozle.org/~neale/src/epy/
|
||||||
|
|
||||||
|
from __future__ import generators
|
||||||
|
import cgi
|
||||||
|
import sys
|
||||||
|
import keyword
|
||||||
|
import token
|
||||||
|
import tokenize
|
||||||
|
|
||||||
|
def prettyprint(fd):
|
||||||
|
"""Pretty print code into HTML/CSS.
|
||||||
|
|
||||||
|
This returns a generator, which generates tokens to be printed to
|
||||||
|
some HTML document. You'll need to define a style sheet to get the
|
||||||
|
colors you like.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
end = (0, 0)
|
||||||
|
last = (None,) * 5
|
||||||
|
for tok in tokenize.generate_tokens(fd.readline):
|
||||||
|
start = tok[2]
|
||||||
|
if start[1] != end[1]:
|
||||||
|
if start[0] != end[0]:
|
||||||
|
# What to do here? Punt.
|
||||||
|
yield '\n<strong>prettyprint punting: %s %s</strong>\n' % (tok, end)
|
||||||
|
else:
|
||||||
|
yield tok[4][end[1]:start[1]]
|
||||||
|
end = tok[3]
|
||||||
|
if end[1] == len(tok[4]):
|
||||||
|
# Prevent punting on newlines
|
||||||
|
end = (end[0] + 1, 0)
|
||||||
|
if tok[0] == token.NAME:
|
||||||
|
if keyword.iskeyword(tok[1]):
|
||||||
|
style = 'KEYWORD'
|
||||||
|
elif last[1] in ('class', 'def'):
|
||||||
|
style = 'FUNCTION'
|
||||||
|
else:
|
||||||
|
style = 'NAME'
|
||||||
|
else:
|
||||||
|
style = token.tok_name.get(tok[0])
|
||||||
|
s = tok[1].expandtabs()
|
||||||
|
txt = cgi.escape(s)
|
||||||
|
if style:
|
||||||
|
last = tok
|
||||||
|
yield ('<span class="%s">%s</span>'
|
||||||
|
% (style, txt))
|
||||||
|
else:
|
||||||
|
yield s
|
||||||
|
|
||||||
|
def pp_fd(f, out=sys.stdout):
|
||||||
|
"""Pretty print a file object."""
|
||||||
|
|
||||||
|
for item in prettyprint(f):
|
||||||
|
out.write(item)
|
||||||
|
|
||||||
|
def pp_file(filename, out=sys.stdout):
|
||||||
|
"""Pretty print a filename.
|
||||||
|
|
||||||
|
Open and pretty-print python source in filename. Output goes to out
|
||||||
|
(default, sys.stdout).
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
pp_fd(open(filename), out)
|
||||||
|
|
||||||
|
def pp_string(string, out=sys.stdout):
|
||||||
|
"""Pretty print a string."""
|
||||||
|
|
||||||
|
import cStringIO as StringIO
|
||||||
|
|
||||||
|
f = StringIO.StringIO(string)
|
||||||
|
pp_fd(f, out)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||||
|
"http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Bee-yoo-ti-ful source code</title>
|
||||||
|
<link rel="stylesheet" type="text/css"
|
||||||
|
href="http://woozle.org/~neale/default.css">
|
||||||
|
</head>
|
||||||
|
<body>'''
|
||||||
|
print '<pre>'
|
||||||
|
# write colorized version to stdout
|
||||||
|
for item in prettyprint(sys.stdin):
|
||||||
|
sys.stdout.write(item)
|
||||||
|
print '</pre></body></html>'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
# Description: Knuth-Morris-Pratt algorithm
|
||||||
|
|
||||||
|
"""Knuth-Morris-Pratt algorithm
|
||||||
|
|
||||||
|
This is a direct transaltion of the KMP algorithm in the book
|
||||||
|
"Introduction to Algorithms" by Cormen, Lieserson, and Rivest. See that
|
||||||
|
book for an explanation of why this algorithm works. It's pretty cool.
|
||||||
|
|
||||||
|
The only things I changed were some offsets, to cope with the fact that
|
||||||
|
Python arrays are 0-offset.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'Neale Pickett <neale@woozle.org>'
|
||||||
|
|
||||||
|
def compute_prefix_function(p):
|
||||||
|
m = len(p)
|
||||||
|
pi = [0] * m
|
||||||
|
k = 0
|
||||||
|
for q in range(1, m):
|
||||||
|
while k > 0 and p[k] != p[q]:
|
||||||
|
k = pi[k - 1]
|
||||||
|
if p[k] == p[q]:
|
||||||
|
k = k + 1
|
||||||
|
pi[q] = k
|
||||||
|
return pi
|
||||||
|
|
||||||
|
def kmp_matcher(t, p):
|
||||||
|
n = len(t)
|
||||||
|
m = len(p)
|
||||||
|
pi = compute_prefix_function(p)
|
||||||
|
q = 0
|
||||||
|
for i in range(n):
|
||||||
|
while q > 0 and p[q] != t[i]:
|
||||||
|
q = pi[q - 1]
|
||||||
|
if p[q] == t[i]:
|
||||||
|
q = q + 1
|
||||||
|
if q == m:
|
||||||
|
return i - m + 1
|
||||||
|
return -1
|
|
@ -0,0 +1,68 @@
|
||||||
|
#! /usr/bin/python
|
||||||
|
|
||||||
|
## cronit -- A lightweight (aka "lousy") anacron replacement
|
||||||
|
## Copyright (C) 2007 Neale Pickett
|
||||||
|
##
|
||||||
|
## 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 3 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.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import popen2
|
||||||
|
import socket
|
||||||
|
|
||||||
|
basedir = os.path.expanduser('~/lib/cron')
|
||||||
|
|
||||||
|
os.chdir(os.path.expanduser('~'))
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
periods = (('test', -1),
|
||||||
|
('hourly', 3600),
|
||||||
|
('daily', 86400),
|
||||||
|
('weekly', 604800),
|
||||||
|
('monthly', 18144000), # more or less
|
||||||
|
('yearly', 2204892000))
|
||||||
|
|
||||||
|
def run_parts(dir):
|
||||||
|
out = []
|
||||||
|
for script in os.listdir(dir):
|
||||||
|
if script.endswith('~') or script.startswith('.'):
|
||||||
|
continue
|
||||||
|
fn = os.path.join(dir, script)
|
||||||
|
proc = popen2.Popen4(fn)
|
||||||
|
proc.tochild.close()
|
||||||
|
outstr = proc.fromchild.read()
|
||||||
|
ret = proc.wait()
|
||||||
|
if outstr or ret:
|
||||||
|
out.append((fn, outstr, ret))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def cronit(basedir):
|
||||||
|
for name, interval in periods:
|
||||||
|
dir = os.path.join(basedir, name)
|
||||||
|
if not os.path.exists(dir):
|
||||||
|
continue
|
||||||
|
tsfile = os.path.join(dir, '.timestamp.%s' % socket.gethostname())
|
||||||
|
try:
|
||||||
|
ts = int(file(tsfile).read().strip())
|
||||||
|
except:
|
||||||
|
ts = 0
|
||||||
|
if ts + interval < now:
|
||||||
|
problems = run_parts(dir)
|
||||||
|
file(tsfile, 'w').write('%d\n' % now)
|
||||||
|
for script, output, ret in problems:
|
||||||
|
print '====== %s exited with code %d' % (script, ret/256)
|
||||||
|
print output
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cronit(basedir)
|
|
@ -0,0 +1,46 @@
|
||||||
|
# maildir.py -- Maildir utilities
|
||||||
|
#
|
||||||
|
# Released into the public domain
|
||||||
|
|
||||||
|
"""Maildir utilities"""
|
||||||
|
|
||||||
|
__author__ = 'Neale Pickett <neale@woozle.org>'
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Counter gets incremented by one for every message delivered
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
# Local hostname
|
||||||
|
HOST = socket.gethostname()
|
||||||
|
|
||||||
|
def create(mdir):
|
||||||
|
os.umask(0022)
|
||||||
|
for i in ('tmp', 'cur', 'new'):
|
||||||
|
os.makedirs('%s/%s' % (mdir, i))
|
||||||
|
|
||||||
|
def write(mdir, message, info=None):
|
||||||
|
"""Write a message out to a maildir.
|
||||||
|
|
||||||
|
"""
|
||||||
|
global count
|
||||||
|
|
||||||
|
mdir = time.strftime('%Y-%m')
|
||||||
|
if not os.path.exists(mdir):
|
||||||
|
maildir_create(mdir)
|
||||||
|
|
||||||
|
filename = '%d.%d_%04d.%s' % (time.time(), os.getpid(),
|
||||||
|
count, HOST)
|
||||||
|
f = open('%s/tmp/%s' % (mdir, filename),
|
||||||
|
'w')
|
||||||
|
f.write(message)
|
||||||
|
f.close()
|
||||||
|
if info:
|
||||||
|
os.rename('%s/tmp/%s' % (mdir, filename),
|
||||||
|
'%s/cur/%s:2,%s' % (mdir, filename, info))
|
||||||
|
else:
|
||||||
|
os.rename('%s/tmp/%s' % (mdir, filename),
|
||||||
|
'%s/new/%s' % (mdir, filename))
|
||||||
|
count += 1
|
|
@ -0,0 +1,54 @@
|
||||||
|
#! /usr/bin/python
|
||||||
|
|
||||||
|
"""ndstrunc -- Trims .nds ROM files."""
|
||||||
|
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
|
||||||
|
parser = optparse.OptionParser(description='Trims .nds ROM files.')
|
||||||
|
parser.add_option('-d', '--dry-run',
|
||||||
|
action='store_false', dest='writeout', default=True,
|
||||||
|
help="Don't actually modify any files.")
|
||||||
|
parser.add_option('-f', '--force',
|
||||||
|
action='store_true', dest='force', default=False,
|
||||||
|
help="Force truncation even if it seems like too much.")
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
for fn in args:
|
||||||
|
f = file(fn, 'rb+')
|
||||||
|
f.seek(0x80)
|
||||||
|
(size,) = struct.unpack('<I', f.read(4))
|
||||||
|
|
||||||
|
ondisk = os.path.getsize(fn)
|
||||||
|
reduction = 100 - (size * 100.0 / ondisk)
|
||||||
|
print fn
|
||||||
|
|
||||||
|
if size == ondisk:
|
||||||
|
print ' Already truncated.'
|
||||||
|
continue
|
||||||
|
# I read in some source code that wifi games need 138 extra bytes.
|
||||||
|
# Why? I don't understand, but I figure you can afford 256 bytes
|
||||||
|
# extra to be on the safe side. Compared to the size of typical ROM
|
||||||
|
# images it's a pittance.
|
||||||
|
size += 256
|
||||||
|
if size >= ondisk:
|
||||||
|
print ' Already truncated.'
|
||||||
|
continue
|
||||||
|
|
||||||
|
print ' On disk:', ondisk
|
||||||
|
print ' Header says:', size
|
||||||
|
print ' Would save: %dB (%2.0f%%)' % (ondisk-size, reduction)
|
||||||
|
if size < (ondisk >> 2):
|
||||||
|
if options.force:
|
||||||
|
print ' Truncating anyway, as requested.'
|
||||||
|
else:
|
||||||
|
print ' Would truncate too much!!'
|
||||||
|
continue
|
||||||
|
if options.writeout:
|
||||||
|
f.truncate(size)
|
||||||
|
print ' Truncated.'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""robotfindskitten -- A zen simulation.
|
||||||
|
|
||||||
|
This is a web version of the classic text-based game. The text-based
|
||||||
|
version is much better. Go check it out at http://robotfindskitten.org/
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import cgitb; cgitb.enable()
|
||||||
|
import cgi
|
||||||
|
import whrandom
|
||||||
|
import sys
|
||||||
|
|
||||||
|
symbols = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$%&\'()*+,-./:;<=>?@[\]^_`{|}~')
|
||||||
|
width = 50
|
||||||
|
height = 30
|
||||||
|
|
||||||
|
def color():
|
||||||
|
def c():
|
||||||
|
return whrandom.choice(('44', '88', 'cc', 'ff'))
|
||||||
|
|
||||||
|
return c() + c() + c()
|
||||||
|
|
||||||
|
print 'Content-type: text/html'
|
||||||
|
print
|
||||||
|
|
||||||
|
def main():
|
||||||
|
nki = []
|
||||||
|
f = open('messages.h')
|
||||||
|
for line in f.xreadlines():
|
||||||
|
if line.startswith(' "'):
|
||||||
|
line = line.strip()
|
||||||
|
if line[-1] == ',':
|
||||||
|
line = line[1:-2]
|
||||||
|
else:
|
||||||
|
line = line[1:-1]
|
||||||
|
line = line.replace(r'\"', '"')
|
||||||
|
nki.append(line)
|
||||||
|
|
||||||
|
print '''<html><head>
|
||||||
|
<title>robotfindskitten: a zen simulation</title>
|
||||||
|
<link rel="stylesheet" type="text/css"
|
||||||
|
href="http://woozle.org/~neale/default.css">
|
||||||
|
<style type="text/css">
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
color: #%s;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: black;
|
||||||
|
border: solid white;
|
||||||
|
text-align: center;
|
||||||
|
padding: .75em;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>''' % color()
|
||||||
|
|
||||||
|
print '<html>'
|
||||||
|
|
||||||
|
print '<h1>robotfindskitten</h1>'
|
||||||
|
|
||||||
|
print '''
|
||||||
|
<p>
|
||||||
|
You are robot. Your mission: find kitten.
|
||||||
|
|
||||||
|
|
||||||
|
Hold the mouse cursor over an object you suspect to be kitten.
|
||||||
|
After a few seconds, your browser will tell you what you have
|
||||||
|
found. Older browsers may not be able to relay what an object
|
||||||
|
actually is, so the experience may be diminished somewhat.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
print '<pre>'
|
||||||
|
|
||||||
|
screen = []
|
||||||
|
for i in range(height):
|
||||||
|
screen.append([' '] * width)
|
||||||
|
|
||||||
|
for i in range(30):
|
||||||
|
x = whrandom.randrange(width)
|
||||||
|
y = whrandom.randrange(height)
|
||||||
|
|
||||||
|
n = whrandom.randrange(len(symbols))
|
||||||
|
s = symbols[n]
|
||||||
|
del symbols[n]
|
||||||
|
s = cgi.escape(s)
|
||||||
|
|
||||||
|
n = whrandom.randrange(len(nki))
|
||||||
|
t = nki[n]
|
||||||
|
del nki[n]
|
||||||
|
t = cgi.escape(t)
|
||||||
|
t = t.replace('"', '"')
|
||||||
|
|
||||||
|
screen[y][x] = ('<span style="color: #%s;" title="%s">%s</span>'
|
||||||
|
% (color(), t, s))
|
||||||
|
# place kitten!
|
||||||
|
screen[y][x] = '<a href="http://robotfindskitten.org" title="You found kitten! Way to go, robot!">%s</a>' % s
|
||||||
|
|
||||||
|
for row in screen:
|
||||||
|
print ''.join(row)
|
||||||
|
|
||||||
|
print '</pre>'
|
||||||
|
|
||||||
|
print '</html>'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
|
@ -0,0 +1,538 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
# Description: SNPP client class for Python
|
||||||
|
|
||||||
|
'''SNPP client class.
|
||||||
|
|
||||||
|
Author: Neale Pickett <neale@woozle.org>
|
||||||
|
|
||||||
|
This was modified from the Python 1.5 library SMTP lib.
|
||||||
|
Which was modified from the Python 1.5 library HTTP lib.
|
||||||
|
|
||||||
|
Basically, I took smtplib, did an s/SMTP/SNPP/, and changed about ten
|
||||||
|
other things. If only all projects were this easy :-)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> import snpplib
|
||||||
|
>>> s = snpplib.SNPP("localhost")
|
||||||
|
>>> print s.help()[1]
|
||||||
|
|
||||||
|
Level 1 commands accepted:
|
||||||
|
|
||||||
|
PAGEr <pager ID>
|
||||||
|
MESSage <alpha or numeric message>
|
||||||
|
RESEt
|
||||||
|
SEND
|
||||||
|
QUIT
|
||||||
|
HELP
|
||||||
|
|
||||||
|
Level 2 commands accepted:
|
||||||
|
|
||||||
|
DATA
|
||||||
|
LOGIn <loginid> [password]
|
||||||
|
LEVEl <ServiceLevel>
|
||||||
|
COVErage <AlternateArea>
|
||||||
|
HOLDuntil <YYMMDDHHMMSS> [+/-GMTdifference]
|
||||||
|
CALLerid <CallerID>
|
||||||
|
|
||||||
|
Level 3 commands accepted:
|
||||||
|
|
||||||
|
none
|
||||||
|
|
||||||
|
OK
|
||||||
|
>>> s.putcmd("rese")
|
||||||
|
>>> s.getreply()
|
||||||
|
(250, 'Reset ok')
|
||||||
|
>>> s.quit()
|
||||||
|
'''
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import re
|
||||||
|
import rfc822
|
||||||
|
import types
|
||||||
|
|
||||||
|
SNPP_PORT = 444
|
||||||
|
CRLF="\r\n"
|
||||||
|
|
||||||
|
# Exception classes used by this module.
|
||||||
|
class SNPPException(Exception):
|
||||||
|
"""Base class for all exceptions raised by this module."""
|
||||||
|
|
||||||
|
class SNPPServerDisconnected(SNPPException):
|
||||||
|
"""Not connected to any SNPP server.
|
||||||
|
|
||||||
|
This exception is raised when the server unexpectedly disconnects,
|
||||||
|
or when an attempt is made to use the SNPP instance before
|
||||||
|
connecting it to a server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class SNPPResponseException(SNPPException):
|
||||||
|
"""Base class for all exceptions that include an SNPP error code.
|
||||||
|
|
||||||
|
These exceptions are generated in some instances when the SNPP
|
||||||
|
server returns an error code. The error code is stored in the
|
||||||
|
`snpp_code' attribute of the error, and the `snpp_error' attribute
|
||||||
|
is set to the error message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, code, msg):
|
||||||
|
self.snpp_code = code
|
||||||
|
self.snpp_error = msg
|
||||||
|
self.args = (code, msg)
|
||||||
|
|
||||||
|
class SNPPSenderRefused(SNPPResponseException):
|
||||||
|
"""Caller ID refused.
|
||||||
|
|
||||||
|
In addition to the attributes set by on all SNPPResponseException
|
||||||
|
exceptions, this sets `sender' to the string that the SNPP refused
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, code, msg, sender):
|
||||||
|
self.snpp_code = code
|
||||||
|
self.snpp_error = msg
|
||||||
|
self.sender = sender
|
||||||
|
self.args = (code, msg, sender)
|
||||||
|
|
||||||
|
class SMTPRecipientsRefused(SNPPResponseException):
|
||||||
|
"""All recipient addresses refused.
|
||||||
|
|
||||||
|
The errors for each recipient are accessable thru the attribute
|
||||||
|
'recipients', which is a dictionary of exactly the same sort as
|
||||||
|
SNPP.sendpage() returns.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, recipients):
|
||||||
|
self.recipients = recipients
|
||||||
|
self.args = ( recipients,)
|
||||||
|
|
||||||
|
|
||||||
|
class SNPPDataError(SNPPResponseException):
|
||||||
|
"""The SNPP server didn't accept the data."""
|
||||||
|
|
||||||
|
class SNPPConnectError(SNPPResponseException):
|
||||||
|
"""Error during connection establishment"""
|
||||||
|
|
||||||
|
def quotedata(data):
|
||||||
|
"""Quote data for email.
|
||||||
|
|
||||||
|
Double leading '.', and change Unix newline '\n', or Mac '\r' into
|
||||||
|
Internet CRLF end-of-line.
|
||||||
|
"""
|
||||||
|
return re.sub(r'(?m)^\.', '..',
|
||||||
|
re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
|
||||||
|
|
||||||
|
class SNPP:
|
||||||
|
"""This class manages a connection to an SNPP server.
|
||||||
|
SNPP Objects:
|
||||||
|
|
||||||
|
For method docs, see each method's docstrings. In general, there is
|
||||||
|
a method of the same name to perform each SNPP command, and there
|
||||||
|
is a method called 'sendpage' that will do an entire page
|
||||||
|
transaction.
|
||||||
|
"""
|
||||||
|
debuglevel = 0
|
||||||
|
file = None
|
||||||
|
|
||||||
|
def __init__(self, host = '', port = 0):
|
||||||
|
"""Initialize a new instance.
|
||||||
|
|
||||||
|
If specified, `host' is the name of the remote host to which to
|
||||||
|
connect. If specified, `port' specifies the port to which to connect.
|
||||||
|
By default, snpplib.SNPP_PORT is used. An SNPPConnectError is
|
||||||
|
raised if the specified `host' doesn't respond correctly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.esnpp_features = {}
|
||||||
|
if host:
|
||||||
|
(code, msg) = self.connect(host, port)
|
||||||
|
if code != 220:
|
||||||
|
raise SNPPConnectError(code, msg)
|
||||||
|
|
||||||
|
def set_debuglevel(self, debuglevel):
|
||||||
|
"""Set the debug output level.
|
||||||
|
|
||||||
|
A non-false value results in debug messages for connection and for all
|
||||||
|
messages sent to and received from the server.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.debuglevel = debuglevel
|
||||||
|
|
||||||
|
def connect(self, host='localhost', port = 0):
|
||||||
|
"""Connect to a host on a given port.
|
||||||
|
|
||||||
|
If the hostname ends with a colon (`:') followed by a number, and
|
||||||
|
there is no port specified, that suffix will be stripped off and the
|
||||||
|
number interpreted as the port number to use.
|
||||||
|
|
||||||
|
Note: This method is automatically invoked by __init__, if a host is
|
||||||
|
specified during instantiation.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not port:
|
||||||
|
i = host.find(':')
|
||||||
|
if i >= 0:
|
||||||
|
host, port = host[:i], host[i+1:]
|
||||||
|
try:
|
||||||
|
port = int(port)
|
||||||
|
except ValueError:
|
||||||
|
raise socket.error, "nonnumeric port"
|
||||||
|
if not port: port = SNPP_PORT
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
if self.debuglevel > 0: print 'connect:', (host, port)
|
||||||
|
self.sock.connect((host, port))
|
||||||
|
(code,msg)=self.getreply()
|
||||||
|
if self.debuglevel >0 : print "connect:", msg
|
||||||
|
return (code,msg)
|
||||||
|
|
||||||
|
def sendstr(self, str):
|
||||||
|
"""Send `str' to the server."""
|
||||||
|
if self.debuglevel > 0: print 'send:', `str`
|
||||||
|
if self.sock:
|
||||||
|
try:
|
||||||
|
self.sock.send(str)
|
||||||
|
except socket.error:
|
||||||
|
raise SNPPServerDisconnected('Server not connected')
|
||||||
|
else:
|
||||||
|
raise SNPPServerDisconnected('please run connect() first')
|
||||||
|
|
||||||
|
def putcmd(self, cmd, args=""):
|
||||||
|
"""Send a command to the server."""
|
||||||
|
str = '%s %s%s' % (cmd, args, CRLF)
|
||||||
|
self.sendstr(str)
|
||||||
|
|
||||||
|
def getreply(self):
|
||||||
|
"""Get a reply from the server.
|
||||||
|
|
||||||
|
Returns a tuple consisting of:
|
||||||
|
|
||||||
|
- server response code (e.g. '250', or such, if all goes well)
|
||||||
|
Note: returns -1 if it can't read response code.
|
||||||
|
|
||||||
|
- server response string corresponding to response code (multiline
|
||||||
|
responses are converted to a single, multiline string).
|
||||||
|
|
||||||
|
Raises SNPPServerDisconnected if end-of-file is reached.
|
||||||
|
"""
|
||||||
|
resp=[]
|
||||||
|
if self.file is None:
|
||||||
|
self.file = self.sock.makefile('rb')
|
||||||
|
while 1:
|
||||||
|
line = self.file.readline()
|
||||||
|
if line == '':
|
||||||
|
self.close()
|
||||||
|
raise SNPPServerDisconnected("Connection unexpectedly closed")
|
||||||
|
if self.debuglevel > 0: print 'reply:', `line`
|
||||||
|
resp.append(line[4:].strip())
|
||||||
|
code=line[:3]
|
||||||
|
# Check that the error code is syntactically correct.
|
||||||
|
# Don't attempt to read a continuation line if it is broken.
|
||||||
|
try:
|
||||||
|
errcode = int(code)
|
||||||
|
except ValueError:
|
||||||
|
errcode = -1
|
||||||
|
break
|
||||||
|
# Check if multiline response.
|
||||||
|
if errcode != 214:
|
||||||
|
break
|
||||||
|
|
||||||
|
errmsg = '\n'.join(resp)
|
||||||
|
if self.debuglevel > 0:
|
||||||
|
print 'reply: retcode (%s); Msg: %s' % (errcode,errmsg)
|
||||||
|
return errcode, errmsg
|
||||||
|
|
||||||
|
def docmd(self, cmd, args=""):
|
||||||
|
"""Send a command, and return its response code."""
|
||||||
|
self.putcmd(cmd,args)
|
||||||
|
return self.getreply()
|
||||||
|
|
||||||
|
def help(self, args=''):
|
||||||
|
"""SNPP 'help' command.
|
||||||
|
Returns help text from server."""
|
||||||
|
return self.docmd("help", args)
|
||||||
|
|
||||||
|
def rese(self):
|
||||||
|
"""SNPP 'rese' command -- resets session."""
|
||||||
|
return self.docmd("rese")
|
||||||
|
|
||||||
|
def page(self, pagerid, password=""):
|
||||||
|
"""SNPP 'page' command -- specifies a pager ID.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if password:
|
||||||
|
args = "%s %s" % (pagerid, password)
|
||||||
|
else:
|
||||||
|
args = pagerid
|
||||||
|
return self.docmd("page", args)
|
||||||
|
|
||||||
|
def mess(self, msg):
|
||||||
|
"""SNPP 'mess' command -- sends a single-line message."""
|
||||||
|
return self.docmd("mess", msg)
|
||||||
|
|
||||||
|
def send(self):
|
||||||
|
"""SNPP 'send' command -- sends a message."""
|
||||||
|
return self.docmd("send")
|
||||||
|
|
||||||
|
def data(self, msg):
|
||||||
|
"""SNPP 'DATA' command -- sends message data to server.
|
||||||
|
|
||||||
|
Automatically quotes lines beginning with a period per rfc821.
|
||||||
|
Raises SNPPDataError if there is an unexpected reply to the
|
||||||
|
DATA command; the return value from this method is the final
|
||||||
|
response code received when the all data is sent.
|
||||||
|
"""
|
||||||
|
self.putcmd("data")
|
||||||
|
(code,repl)=self.getreply()
|
||||||
|
if self.debuglevel >0 : print "data:", (code,repl)
|
||||||
|
if code <> 354:
|
||||||
|
raise SNPPDataError(code,repl)
|
||||||
|
else:
|
||||||
|
self.sendstr(quotedata(msg))
|
||||||
|
self.sendstr("%s.%s" % (CRLF, CRLF))
|
||||||
|
(code,msg)=self.getreply()
|
||||||
|
if self.debuglevel >0 : print "data:", (code,msg)
|
||||||
|
return (code,msg)
|
||||||
|
|
||||||
|
# Level 2 - Optional Extensions
|
||||||
|
|
||||||
|
def logi(self, loginid, password=""):
|
||||||
|
"""SNPP 'logi' command -- logs in to the server.
|
||||||
|
|
||||||
|
This command allows for a session login ID to be specified. It
|
||||||
|
is used to validate the person attempting to access the paging
|
||||||
|
terminal. If no LOGIn command is issued, "anonymous" user
|
||||||
|
status is assumed.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if password:
|
||||||
|
args = "%s %s" % (loginid, password)
|
||||||
|
else:
|
||||||
|
args = loginid
|
||||||
|
return self.docmd("logi", args)
|
||||||
|
|
||||||
|
def leve(self, servicelevel):
|
||||||
|
"""SNPP 'leve' command -- sets server level for next PAGE.
|
||||||
|
|
||||||
|
The LEVEl function is used to specify an optional alternate
|
||||||
|
level of service for the next PAGEr command. Ideally,
|
||||||
|
"ServiceLevel" should be an integer between 0 and 11 inclusive.
|
||||||
|
The TME protocol specifies ServiceLevel as follows:
|
||||||
|
|
||||||
|
0 - Priority
|
||||||
|
1 - Normal (default)
|
||||||
|
2 - Five minutes
|
||||||
|
3 - Fifteen minutes
|
||||||
|
4 - One hour
|
||||||
|
5 - Four hours
|
||||||
|
6 - Twelve hours
|
||||||
|
7 - Twenty Four hours
|
||||||
|
8 - Carrier specific '1'
|
||||||
|
9 - Carrier specific '2'
|
||||||
|
10 - Carrier specific '3'
|
||||||
|
11 - Carrier specific '4'
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.docmd("leve", servicelevel)
|
||||||
|
|
||||||
|
def aler(self, alertoverride):
|
||||||
|
"""SNPP 'aler' command -- override alert setting.
|
||||||
|
|
||||||
|
The optional ALERt command may be used to override the default
|
||||||
|
setting and specify whether or not to alert the subscriber upon
|
||||||
|
receipt of a message. This option, like the previous command,
|
||||||
|
alters the parameters submitted to the paging terminal using
|
||||||
|
the PAGEr command. The TME protocol specifies AlertOverride as
|
||||||
|
either 0-DoNotAlert, or 1-Alert.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.docmd("aler", alertoverride)
|
||||||
|
|
||||||
|
def cove(self, alternatearea):
|
||||||
|
"""SNPP 'cove' command -- override coverage area.
|
||||||
|
|
||||||
|
The optional COVErage command is used to override the
|
||||||
|
subscriber's default coverage area, and allow for the selection
|
||||||
|
of an alternate region. This option, like the previous
|
||||||
|
command, alters the parameters submitted to the paging terminal
|
||||||
|
using the PAGEr command. AlternateArea is a designator for one
|
||||||
|
of the following:
|
||||||
|
|
||||||
|
- A subscriber-specific alternate coverage area
|
||||||
|
- A carrier-defined region available to subscribers
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.docmd("cove", alternatearea)
|
||||||
|
|
||||||
|
def hold(self, timespec, gmtdiff=""):
|
||||||
|
"""SNPP 'hold' command -- hold until specified time.
|
||||||
|
|
||||||
|
The HOLDuntil command allows for the delayed delivery of a
|
||||||
|
message, to a particular subscriber, until after the time
|
||||||
|
specified. The time may be specified in local time (e.g. local
|
||||||
|
to the paging terminal), or with an added parameter specifying
|
||||||
|
offset from GMT (in other words, "-0600" specifies Eastern
|
||||||
|
Standard Time). This option, like the previous command, alters
|
||||||
|
the parameters submitted to the paging terminal using the PAGEr
|
||||||
|
command.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if gmtdiff:
|
||||||
|
args = "%s %s" % (timespec, gmtdiff)
|
||||||
|
else:
|
||||||
|
args = timespec
|
||||||
|
|
||||||
|
return self.docmd("hold", args)
|
||||||
|
|
||||||
|
def call(self, callerid):
|
||||||
|
"""SNPP 'call' command -- specify caller ID.
|
||||||
|
|
||||||
|
The CALLerid function is a message-oriented function (as opposed
|
||||||
|
to the subscriber-oriented functions just described). This
|
||||||
|
allows for the specification of the CallerIdentifier function as
|
||||||
|
described in TME. This parameter is optional, and is at the
|
||||||
|
discretion of the carrier as to how it should be implemented or
|
||||||
|
used.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.docmd("call", callerid)
|
||||||
|
|
||||||
|
def subj(self, messagesubject):
|
||||||
|
"""SNPP 'subj' command -- specify a message subject.
|
||||||
|
|
||||||
|
The SUBJect function allows is a message-oriented function that
|
||||||
|
allows the sender to specify a subject for the next message to
|
||||||
|
be sent. This parameter is optional and is at the discretion of
|
||||||
|
the carrier as to how it should be implemented or used.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.docmd("subj", messagesubject)
|
||||||
|
|
||||||
|
|
||||||
|
# Level 3 -- Two-Way Extensions
|
||||||
|
|
||||||
|
# I didn't implement these because I got tired of typing this stuff
|
||||||
|
# in, and I doubt anyone's going to use this anyhow, seeing as how
|
||||||
|
# qpage doesn't do any of these extensions. Although this may
|
||||||
|
# change.
|
||||||
|
|
||||||
|
# some useful methods
|
||||||
|
def sendpage(self, from_id, to_ids, msg):
|
||||||
|
"""This command performs an entire mail transaction.
|
||||||
|
|
||||||
|
The arguments are:
|
||||||
|
- from_id : The CallerID sending this mail, or None to
|
||||||
|
not send any CallerID.
|
||||||
|
- to_ids : A list of pager IDs to send this page to. A
|
||||||
|
bare string will be treated as a list with 1
|
||||||
|
address.
|
||||||
|
- msg : The message to send.
|
||||||
|
|
||||||
|
This method will return normally if the page is accepted for at
|
||||||
|
least one recipient. It returns a dictionary, with one entry for
|
||||||
|
each recipient that was refused. Each entry contains a tuple of
|
||||||
|
the SNPP error code and the accompanying error message sent by
|
||||||
|
the server.
|
||||||
|
|
||||||
|
This method may raise the following exceptions:
|
||||||
|
|
||||||
|
SNPPRecipientsRefused The server rejected for ALL recipients
|
||||||
|
(no mail was sent).
|
||||||
|
SNPPSenderRefused The server didn't accept the from_id.
|
||||||
|
SNPPDataError The server replied with an unexpected
|
||||||
|
error code
|
||||||
|
|
||||||
|
Note: the connection will be open even after an exception is raised.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> import snpplib
|
||||||
|
>>> s=snpplib.SNPP("localhost")
|
||||||
|
>>> tolist=["parsingh","rhiannon","saffola","bob"]
|
||||||
|
>>> msg = 'Hey guys, what say we have pizza for dinner?'
|
||||||
|
>>> s.sendpage("neale",tolist,msg)
|
||||||
|
{ "bob" : ( 550 ,"User unknown" ) }
|
||||||
|
>>> s.quit()
|
||||||
|
|
||||||
|
In the above example, the message was accepted for delivery to three
|
||||||
|
of the four addresses, and one was rejected, with the error code
|
||||||
|
550. If all addresses are accepted, then the method will return an
|
||||||
|
empty dictionary.
|
||||||
|
|
||||||
|
"""
|
||||||
|
(code,resp) = self.call(from_id)
|
||||||
|
if code <> 250:
|
||||||
|
# CallerID command not implemented, too bad.
|
||||||
|
pass
|
||||||
|
senderrs={}
|
||||||
|
if type(to_ids) == types.StringType:
|
||||||
|
to_ids = [to_ids]
|
||||||
|
for each in to_ids:
|
||||||
|
(code,resp)=self.page(each)
|
||||||
|
if (code <> 250) and (code <> 251):
|
||||||
|
senderrs[each]=(code,resp)
|
||||||
|
if len(senderrs)==len(to_ids):
|
||||||
|
# the server refused all our recipients
|
||||||
|
self.rese()
|
||||||
|
raise SNPPRecipientsRefused(senderrs)
|
||||||
|
(code,resp)=self.data(msg)
|
||||||
|
if code <> 250:
|
||||||
|
self.rese()
|
||||||
|
raise SNPPDataError(code, resp)
|
||||||
|
(code,resp)=self.send()
|
||||||
|
if code <> 250:
|
||||||
|
self.rese()
|
||||||
|
raise SNPPDataError(code, resp)
|
||||||
|
#if we got here then somebody got our page
|
||||||
|
return senderrs
|
||||||
|
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close the connection to the SNPP server."""
|
||||||
|
if self.file:
|
||||||
|
self.file.close()
|
||||||
|
self.file = None
|
||||||
|
if self.sock:
|
||||||
|
self.sock.close()
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
|
||||||
|
def quit(self):
|
||||||
|
"""Terminate the SNPP session."""
|
||||||
|
self.docmd("quit")
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
# Test the sendmail method, which tests most of the others.
|
||||||
|
# Note: This always sends to localhost.
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys, rfc822
|
||||||
|
|
||||||
|
def prompt(prompt):
|
||||||
|
sys.stdout.write(prompt + ": ")
|
||||||
|
return sys.stdin.readline().strip()
|
||||||
|
|
||||||
|
fromaddr = prompt("From")
|
||||||
|
toaddrs = prompt("To").split(',')
|
||||||
|
print "Enter message, end with ^D:"
|
||||||
|
msg = ''
|
||||||
|
while 1:
|
||||||
|
line = sys.stdin.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
msg = msg + line
|
||||||
|
print "Message length is " + `len(msg)`
|
||||||
|
|
||||||
|
server = SNPP('localhost')
|
||||||
|
server.set_debuglevel(1)
|
||||||
|
server.sendpage(fromaddr, toaddrs, msg)
|
||||||
|
server.quit()
|
|
@ -0,0 +1,325 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
### spampot.py -- Spam honeypot SMTP server
|
||||||
|
### Copyright (C) 2003 Neale Pikett <neale@woozle.org>
|
||||||
|
### Time-stamp: <2003-05-06 09:08:52 neale>
|
||||||
|
###
|
||||||
|
### This 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, 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.
|
||||||
|
###
|
||||||
|
### You should have received a copy of the GNU General Public License
|
||||||
|
### along with this software; see the file COPYING. If not, write to
|
||||||
|
### the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
|
||||||
|
### USA.
|
||||||
|
|
||||||
|
"""Spam honeypot SMTP server.
|
||||||
|
|
||||||
|
This just sits on port 25 of whatever IP you pass in as an argument, and
|
||||||
|
spools every message out to MAILDIR. It tries to look like an old
|
||||||
|
Sendmail server, to maximize chances of being tagged as an open relay.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import cStringIO as StringIO
|
||||||
|
import asynchat
|
||||||
|
import asyncore
|
||||||
|
import syslog
|
||||||
|
import smtplib
|
||||||
|
import rfc822
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import maildir # get maildir.py from the same place you got this file
|
||||||
|
|
||||||
|
# suid to this user
|
||||||
|
USER = 'spam'
|
||||||
|
|
||||||
|
# host to relay probes for us
|
||||||
|
SMARTHOST = '127.0.0.1'
|
||||||
|
|
||||||
|
# save multiple messages from the same IP? You probably don't want this
|
||||||
|
# -- it can consume a gigabyte a day.
|
||||||
|
SAVEDUPES = False
|
||||||
|
|
||||||
|
# slow down if multiple mails are getting sent over a single connection?
|
||||||
|
# This could save bandwidth.
|
||||||
|
TARPIT = True
|
||||||
|
|
||||||
|
# chroot to this directory and spool messages there
|
||||||
|
MAILDIR = '/home/spam/Maildir/spampot'
|
||||||
|
|
||||||
|
# My hostname
|
||||||
|
HOST = socket.gethostname()
|
||||||
|
|
||||||
|
# write to this PID file
|
||||||
|
PIDFILE = '/var/run/spampot.pid'
|
||||||
|
|
||||||
|
# syslog levels (you shouldn't need to change this)
|
||||||
|
LEVELS = {'info': syslog.LOG_INFO,
|
||||||
|
'warning': syslog.LOG_WARNING,
|
||||||
|
'error': syslog.LOG_ERR}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# Hosts seen
|
||||||
|
seen = {}
|
||||||
|
|
||||||
|
def shescape(str):
|
||||||
|
return "'" + str.replace("'", "'\"'\"'") + "'"
|
||||||
|
|
||||||
|
|
||||||
|
class Daemon:
|
||||||
|
"""Helpful class to make a process a daemon"""
|
||||||
|
|
||||||
|
def __init__(self, pidfile):
|
||||||
|
try:
|
||||||
|
f = file(pidfile, "r")
|
||||||
|
pid = int(f.read())
|
||||||
|
f.close()
|
||||||
|
os.kill(pid, 0)
|
||||||
|
print "Already running at pid %d" % pid
|
||||||
|
sys.exit(1)
|
||||||
|
except (IOError, OSError, ValueError):
|
||||||
|
pass
|
||||||
|
self.pidf = file(pidfile, 'w')
|
||||||
|
|
||||||
|
def daemonize(self):
|
||||||
|
self.pid = os.fork()
|
||||||
|
if self.pid:
|
||||||
|
self.pidf.write("%d\n" % self.pid)
|
||||||
|
sys.exit(0)
|
||||||
|
# Decouple from parent
|
||||||
|
self.pidf.close()
|
||||||
|
os.chdir("/")
|
||||||
|
os.setsid()
|
||||||
|
os.umask(0)
|
||||||
|
os.close(sys.stdin.fileno())
|
||||||
|
os.close(sys.stdout.fileno())
|
||||||
|
os.close(sys.stderr.fileno())
|
||||||
|
syslog.syslog(syslog.LOG_INFO, "starting")
|
||||||
|
return self.pid
|
||||||
|
|
||||||
|
def jail(self, root, user=None, group=None):
|
||||||
|
uid, gid = None, None
|
||||||
|
if group:
|
||||||
|
import grp
|
||||||
|
|
||||||
|
gr = grp.getgrnam(group)
|
||||||
|
gid = gr[2]
|
||||||
|
if user:
|
||||||
|
import pwd
|
||||||
|
|
||||||
|
pw = pwd.getpwnam(user)
|
||||||
|
uid = pw[2]
|
||||||
|
if not gid: gid = pw[3]
|
||||||
|
os.chroot(root)
|
||||||
|
os.chdir('/')
|
||||||
|
if gid: os.setgid(gid)
|
||||||
|
if uid: os.setuid(uid)
|
||||||
|
|
||||||
|
|
||||||
|
class Listener(asyncore.dispatcher):
|
||||||
|
"""Listens for incoming socket connections and spins off
|
||||||
|
dispatchers created by a factory callable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, bindaddr, port,
|
||||||
|
factory, factoryArgs=()):
|
||||||
|
asyncore.dispatcher.__init__(self)
|
||||||
|
self.factory = factory
|
||||||
|
self.factoryArgs = factoryArgs
|
||||||
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.set_reuse_addr()
|
||||||
|
self.bind((bindaddr, port))
|
||||||
|
self.listen(40)
|
||||||
|
syslog.syslog(syslog.LOG_INFO, 'Listening on %s:%d' % (bindaddr, port))
|
||||||
|
|
||||||
|
def handle_accept(self):
|
||||||
|
# If an incoming connection is instantly reset, eg. by following a
|
||||||
|
# link in the web interface then instantly following another one or
|
||||||
|
# hitting stop, handle_accept() will be triggered but accept() will
|
||||||
|
# return None.
|
||||||
|
result = self.accept()
|
||||||
|
if result:
|
||||||
|
clientSocket, clientAddress = result
|
||||||
|
args = [clientSocket] + list(self.factoryArgs)
|
||||||
|
self.factory(*args)
|
||||||
|
|
||||||
|
|
||||||
|
class Server(asynchat.async_chat):
|
||||||
|
"""A stupid SMTP server."""
|
||||||
|
|
||||||
|
def __init__(self, sock):
|
||||||
|
self.msg_count = 0
|
||||||
|
self.host = 'internal.nat'
|
||||||
|
self.request = ''
|
||||||
|
self.hello = None
|
||||||
|
self.reset()
|
||||||
|
asynchat.async_chat.__init__(self)
|
||||||
|
self.set_socket(sock)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.mailfrom = None
|
||||||
|
self.rcptto = []
|
||||||
|
|
||||||
|
def log(self, message):
|
||||||
|
syslog.syslog(syslog.LOG_INFO, message)
|
||||||
|
|
||||||
|
def log_info(self, message, type='info'):
|
||||||
|
lvl = LEVELS.get(type, syslog.LOG_INFO)
|
||||||
|
syslog.syslog(lvl, message)
|
||||||
|
|
||||||
|
def handle_connect(self):
|
||||||
|
self.peername = self.getpeername()
|
||||||
|
self.sockname = self.getsockname()
|
||||||
|
self.socknamehex = "%X" % struct.unpack('L', socket.inet_aton(self.sockname[0]))
|
||||||
|
self.set_terminator('\r\n')
|
||||||
|
|
||||||
|
self.log('Connect from %s' % (self.peername,))
|
||||||
|
now = time.localtime()
|
||||||
|
ts = time.strftime('%a, ' + str(now[2]) + ' %b %y %H:%M:%S %Z')
|
||||||
|
self.push("220 %s Sendmail ready at %s\r\n" % (self.host, ts))
|
||||||
|
|
||||||
|
def handle_close(self):
|
||||||
|
self.log('Close from %s; relayed %d messages' % (self.peername, self.msg_count))
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def collect_incoming_data(self, data):
|
||||||
|
self.request += data
|
||||||
|
|
||||||
|
def envelope_found_terminator(self):
|
||||||
|
data = self.request
|
||||||
|
self.request = ""
|
||||||
|
command = data[:4].upper()
|
||||||
|
if command in ["HELO", "EHLO"]:
|
||||||
|
whom = data[5:].strip()
|
||||||
|
self.hello = whom
|
||||||
|
self.push("250 Hello %s, pleased to meet you.\r\n" % whom)
|
||||||
|
elif command == 'MAIL':
|
||||||
|
whom = data[10:].strip()
|
||||||
|
self.mailfrom = whom
|
||||||
|
self.push("250 %s... Sender ok\r\n" % whom)
|
||||||
|
elif command == 'RCPT':
|
||||||
|
whom = data[8:].strip()
|
||||||
|
self.rcptto.append(whom)
|
||||||
|
self.push("250 %s... Recipient ok\r\n" % whom)
|
||||||
|
elif command == "DATA":
|
||||||
|
self.set_terminator('\r\n.\r\n')
|
||||||
|
self.found_terminator = self.data_found_terminator
|
||||||
|
self.push('354 Enter mail, end with "." on a line by itself\r\n')
|
||||||
|
elif command == "QUIT":
|
||||||
|
self.push("221 %s closing connection\r\n" % self.host)
|
||||||
|
self.close_when_done()
|
||||||
|
elif command == "RSET":
|
||||||
|
self.reset()
|
||||||
|
self.push('250 Reset state\r\n')
|
||||||
|
else:
|
||||||
|
self.push("500 Command unrecognized\r\n")
|
||||||
|
found_terminator = envelope_found_terminator
|
||||||
|
|
||||||
|
def data_found_terminator(self):
|
||||||
|
self.message = self.request
|
||||||
|
self.request = ''
|
||||||
|
self.set_terminator('\r\n')
|
||||||
|
self.found_terminator = self.envelope_found_terminator
|
||||||
|
self.deliver_message()
|
||||||
|
self.push("250 Mail accepted\r\n")
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def relay_message(self):
|
||||||
|
s = os.popen("/usr/lib/sendmail -f %s %s"
|
||||||
|
% (shescape(self.string),
|
||||||
|
' '.join([shescape(s) for s in self.rcptto])),
|
||||||
|
'w')
|
||||||
|
s.write(self.message)
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
probe_re = None
|
||||||
|
|
||||||
|
def is_probe(self):
|
||||||
|
"""Returns true if the current message is a probe message"""
|
||||||
|
|
||||||
|
# Compile the probe regular expression the first time through
|
||||||
|
if not self.probe_re:
|
||||||
|
self.probe_re = re.compile(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|%s:25' % self.socknamehex,
|
||||||
|
re.IGNORECASE)
|
||||||
|
|
||||||
|
# If it's not the first message this connection, it's probably
|
||||||
|
# not a probe.
|
||||||
|
if self.msg_count:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Probes also don't have multiple recipients
|
||||||
|
if len(self.rcptto) != 1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# And they're short
|
||||||
|
if len(self.message) > 1024:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check for the probe regex
|
||||||
|
if self.probe_re.search(self.message):
|
||||||
|
# we have a bite: now do some more intense investigation
|
||||||
|
f = StringIO.StringIO(self.message)
|
||||||
|
m = rfc822.Message(f)
|
||||||
|
|
||||||
|
# IP address in subject?
|
||||||
|
subj = m.get('Subject')
|
||||||
|
if subj and subj.find(self.sockname[0]) != -1:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Hex-encoded IP address anywhere in message?
|
||||||
|
if m.find(self.socknamehex) != -1:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def deliver_message(self):
|
||||||
|
global seen
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
"SMTP-Date: %s" % time.ctime(),
|
||||||
|
"SMTP-Sock: %s:%d" % self.sockname,
|
||||||
|
"SMTP-Peer: %s:%d" % self.peername,
|
||||||
|
"SMTP-Hello: %s" % self.hello,
|
||||||
|
"SMTP-Mail-From: %s" % self.mailfrom,
|
||||||
|
"SMTP-Messages-This-Connection: %s" % self.msg_count,
|
||||||
|
]
|
||||||
|
for t in self.rcptto:
|
||||||
|
headers.append("SMTP-Rcpt-To: %s" % t)
|
||||||
|
if self.is_probe():
|
||||||
|
self.relay_message()
|
||||||
|
self.log('Relayed probe from=%s to=%s' % (self.mailfrom, self.rcptto))
|
||||||
|
headers.append("SMTP-Relayed: Yes")
|
||||||
|
msg_count = seen.get(self.peername) + 1
|
||||||
|
seen[self.peername] = msg_count
|
||||||
|
if msg_count in (0, 1, 2, 3, 4, 8, 64, 512, 4096, 32768, 262144):
|
||||||
|
# Hopefully nobody running this will ever hit that last one ;)
|
||||||
|
msg = '\r\n'.join(headers) + '\r\n' + self.message
|
||||||
|
m = maildir.write(time.strftime('%Y-%m'), msg)
|
||||||
|
self.log('Trapped from=%s to=%s msg_count=%d' % (self.mailfrom, self.rcptto, msg_count))
|
||||||
|
self.msg_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
dmn = Daemon(PIDFILE)
|
||||||
|
syslog.openlog('spampot', syslog.LOG_PID, syslog.LOG_MAIL)
|
||||||
|
listener = Listener(sys.argv[1], 25, Server)
|
||||||
|
dmn.jail(MAILDIR, USER)
|
||||||
|
dmn.daemonize()
|
||||||
|
asyncore.loop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
|
@ -0,0 +1,329 @@
|
||||||
|
#! /usr/bin/python
|
||||||
|
|
||||||
|
"""Status dealiemajigger for dzen2 (or others, probably).
|
||||||
|
|
||||||
|
This outputs:
|
||||||
|
|
||||||
|
* Debian packages in need of upgrade (if you have the apt module)
|
||||||
|
* master volume (if you have alsa)
|
||||||
|
* battery life (if you have a battery)
|
||||||
|
* load average if it goes over 0.25
|
||||||
|
* current date and time
|
||||||
|
|
||||||
|
It prints it again whenever any of these change. It also watches for Mute,
|
||||||
|
Volume Up, and Volume Down key events on multimedia keyboards, and adjusts
|
||||||
|
the mixer appropriately.
|
||||||
|
|
||||||
|
Lastly, it registers itself with D-Bus so you can send yourself notices
|
||||||
|
like so:
|
||||||
|
|
||||||
|
$ dbus-send --dest=org.woozle.Status \
|
||||||
|
/org/woozle/Status \
|
||||||
|
org.woozle.Status.notice \
|
||||||
|
string:'this is where your message goes'
|
||||||
|
|
||||||
|
They will show up green in dzen2.
|
||||||
|
|
||||||
|
This only polls the ALSA mixer settings, and those only every minute.
|
||||||
|
Master mixer changes will take from 0 to 60 seconds to register if you
|
||||||
|
don't use the multimedia keys on your keyboard.
|
||||||
|
|
||||||
|
You need to be running HAL for this to do much other than tell you
|
||||||
|
the time. Don't worry, HAL is pretty small.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import signal
|
||||||
|
import dbus
|
||||||
|
import dbus.service
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import gobject
|
||||||
|
import time
|
||||||
|
from sets import Set
|
||||||
|
try:
|
||||||
|
import apt
|
||||||
|
import socket
|
||||||
|
except ImportError:
|
||||||
|
apt = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import alsaaudio
|
||||||
|
except ImportError:
|
||||||
|
alsaaudio = None
|
||||||
|
|
||||||
|
# Set to true if you want dzen2 colors
|
||||||
|
dzen2 = False
|
||||||
|
|
||||||
|
def color(name, text):
|
||||||
|
if dzen2:
|
||||||
|
return '^fg(%s)%s^fg()' % (name, text)
|
||||||
|
else:
|
||||||
|
return '-=[ %s ]=-' % text
|
||||||
|
|
||||||
|
class Status(dbus.service.Object):
|
||||||
|
debug = False
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
dbus.service.Object.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
self.system_bus = dbus.SystemBus()
|
||||||
|
|
||||||
|
self.hal_manager = self.system_bus.get_object('org.freedesktop.Hal',
|
||||||
|
'/org/freedesktop/Hal/Manager')
|
||||||
|
self.hal_computer = self.system_bus.get_object('org.freedesktop.Hal',
|
||||||
|
'/org/freedesktop/Hal/devices/computer')
|
||||||
|
|
||||||
|
# Listen for HAL events
|
||||||
|
self.system_bus.add_signal_receiver(self.handle_hal_event,
|
||||||
|
dbus_interface='org.freedesktop.Hal.Device',
|
||||||
|
path_keyword='path',
|
||||||
|
member_keyword='member')
|
||||||
|
|
||||||
|
# For now I only care about battery 0.
|
||||||
|
batteries = self.hal_manager.FindDeviceByCapability('battery',
|
||||||
|
dbus_interface='org.freedesktop.Hal.Manager')
|
||||||
|
if batteries:
|
||||||
|
bat = self.system_bus.get_object('org.freedesktop.Hal',
|
||||||
|
batteries[0])
|
||||||
|
self.battery = dbus.Interface(bat,
|
||||||
|
dbus_interface='org.freedesktop.Hal.Device')
|
||||||
|
else:
|
||||||
|
self.battery = None
|
||||||
|
|
||||||
|
# Audio mixer, if we have the ALSA module installed.
|
||||||
|
if alsaaudio:
|
||||||
|
try:
|
||||||
|
self.mixer = alsaaudio.Mixer()
|
||||||
|
except alsaaudio.ALSAAudioError:
|
||||||
|
# Some weird mixers have no Master control (eg. Mac Mini)
|
||||||
|
self.mixer = alsaaudio.Mixer('PCM')
|
||||||
|
except ImportError:
|
||||||
|
self.mixer = None
|
||||||
|
else:
|
||||||
|
self.mixer = None
|
||||||
|
|
||||||
|
# How many debian packages can be upgraded?
|
||||||
|
self.upgradable = []
|
||||||
|
|
||||||
|
# All my children
|
||||||
|
self.pids = Set()
|
||||||
|
|
||||||
|
def run(self, file, *args):
|
||||||
|
pid = os.spawnlp(os.P_NOWAIT, file, file, *args)
|
||||||
|
self.pids.add(pid)
|
||||||
|
|
||||||
|
def battery_level(self):
|
||||||
|
if self.battery:
|
||||||
|
return self.battery.GetProperty('battery.charge_level.percentage')
|
||||||
|
|
||||||
|
def battery_capacity_state(self):
|
||||||
|
if self.battery:
|
||||||
|
try:
|
||||||
|
return self.battery.GetProperty('battery.charge_level.capacity_state')
|
||||||
|
except dbus.exceptions.DBusException:
|
||||||
|
bat = self.battery_level()
|
||||||
|
if bat > 20:
|
||||||
|
return 'ok'
|
||||||
|
else:
|
||||||
|
return 'battery low'
|
||||||
|
|
||||||
|
def suspend(self, num_secs_to_wakeup=0):
|
||||||
|
def ignore(*d):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.run('screenlock')
|
||||||
|
self.hal_computer.Suspend(num_secs_to_wakeup,
|
||||||
|
dbus_interface='org.freedesktop.Hal.Device.SystemPowerManagement',
|
||||||
|
reply_handler=ignore,
|
||||||
|
error_handler=ignore)
|
||||||
|
|
||||||
|
def muted(self):
|
||||||
|
if self.mixer:
|
||||||
|
try:
|
||||||
|
return self.mixer.getmute()[0]
|
||||||
|
except alsaaudio.ALSAAudioError:
|
||||||
|
pass
|
||||||
|
raise ValueError('No mute control')
|
||||||
|
|
||||||
|
def toggle_mute(self):
|
||||||
|
try:
|
||||||
|
mute = self.muted()
|
||||||
|
self.mixer.setmute(not mute)
|
||||||
|
self.update()
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dbus.service.method('org.woozle.Status',
|
||||||
|
in_signature='d')
|
||||||
|
def adjust_volume(self, adj):
|
||||||
|
if self.mixer:
|
||||||
|
vol = self.mixer.getvolume()[0]
|
||||||
|
vol += adj
|
||||||
|
vol = min(vol, 100)
|
||||||
|
vol = max(vol, 0)
|
||||||
|
self.mixer.setvolume(vol)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def volume(self):
|
||||||
|
if self.mixer:
|
||||||
|
try:
|
||||||
|
mute = self.muted()
|
||||||
|
except ValueError:
|
||||||
|
mute = False
|
||||||
|
if mute:
|
||||||
|
return '--'
|
||||||
|
else:
|
||||||
|
return str(self.mixer.getvolume()[0])
|
||||||
|
|
||||||
|
def handle_button_pressed(self, button, path):
|
||||||
|
if button == 'lid':
|
||||||
|
lid = self.system_bus.get_object('org.freedesktop.Hal',
|
||||||
|
path)
|
||||||
|
if (lid.GetProperty('button.state.value',
|
||||||
|
dbus_interface='org.freedesktop.Hal.Device') and
|
||||||
|
not os.path.exists(os.path.expanduser('~/nosusp'))):
|
||||||
|
self.suspend()
|
||||||
|
elif button == 'mute':
|
||||||
|
self.toggle_mute()
|
||||||
|
elif button == 'volume-down':
|
||||||
|
self.adjust_volume(-5)
|
||||||
|
elif button == 'volume-up':
|
||||||
|
self.adjust_volume(+5)
|
||||||
|
elif button == 'sleep':
|
||||||
|
self.suspend()
|
||||||
|
elif self.debug:
|
||||||
|
self.update('button pressed: %s' % button)
|
||||||
|
|
||||||
|
def handle_property_modified(self, name, added, removed):
|
||||||
|
if name == 'battery.charge_level.percentage':
|
||||||
|
self.update()
|
||||||
|
elif name.startswith('battery.'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.update('property modified: %s' % name)
|
||||||
|
|
||||||
|
def handle_hal_event(self, *d, **k):
|
||||||
|
member = k.get('member')
|
||||||
|
if member == 'Condition':
|
||||||
|
if d[0] == 'ButtonPressed':
|
||||||
|
self.handle_button_pressed(d[1], k.get('path'))
|
||||||
|
elif member == 'PropertyModified':
|
||||||
|
assert int(d[0]) == len(d[1])
|
||||||
|
for p in d[1]:
|
||||||
|
self.handle_property_modified(*p)
|
||||||
|
else:
|
||||||
|
self.update('%s: %s' % (d, kwargs))
|
||||||
|
|
||||||
|
def update(self, msg=None):
|
||||||
|
out = []
|
||||||
|
if msg:
|
||||||
|
out.append(color('green', msg))
|
||||||
|
|
||||||
|
if self.upgradable:
|
||||||
|
out.append(color('cyan', '%d upgradable packages' % self.upgradable))
|
||||||
|
|
||||||
|
la = file('/proc/loadavg').read()
|
||||||
|
load = la.split()[0]
|
||||||
|
if float(load) > 0.2:
|
||||||
|
out.append('L %s' % load)
|
||||||
|
|
||||||
|
vol = self.volume()
|
||||||
|
if vol:
|
||||||
|
out.append('V %s' % vol)
|
||||||
|
|
||||||
|
bat = self.battery_level()
|
||||||
|
if bat:
|
||||||
|
out.append('B %s%%' % bat)
|
||||||
|
|
||||||
|
cap_state = self.battery_capacity_state()
|
||||||
|
if cap_state != 'ok':
|
||||||
|
out.append(color('green', cap))
|
||||||
|
|
||||||
|
out.append(time.strftime('%b %e %l:%M%P'))
|
||||||
|
|
||||||
|
print ' '.join(out)
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(0, os.WNOHANG)
|
||||||
|
self.pids.remove(pid)
|
||||||
|
except (OSError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@dbus.service.method('org.woozle.Status',
|
||||||
|
in_signature='s')
|
||||||
|
def notice(self, msg):
|
||||||
|
self.update(msg)
|
||||||
|
|
||||||
|
@dbus.service.method('org.woozle.Status',
|
||||||
|
in_signature='')
|
||||||
|
def restart(self):
|
||||||
|
re_exec()
|
||||||
|
|
||||||
|
@dbus.service.method('org.woozle.Status',
|
||||||
|
in_signature='b')
|
||||||
|
def set_debug(self, debug):
|
||||||
|
self.debug = debug
|
||||||
|
|
||||||
|
def debcheck(self):
|
||||||
|
# I used to do this in a thread. Unfortunately, this expensive
|
||||||
|
# operation kept the entire apt cache around in RAM forever,
|
||||||
|
# making the status program the biggest memory user on my entire
|
||||||
|
# machine. That offends my sense of aesthetics, so now we fork
|
||||||
|
# and read from a pipe.
|
||||||
|
def cb(source, cb_condition):
|
||||||
|
s = source.recv(8192)
|
||||||
|
self.upgradable = int(s)
|
||||||
|
return False
|
||||||
|
|
||||||
|
a,b = socket.socketpair()
|
||||||
|
if os.fork():
|
||||||
|
gobject.io_add_watch(a, gobject.IO_IN, cb)
|
||||||
|
else:
|
||||||
|
l = [p for p in apt.Cache() if p.isUpgradable]
|
||||||
|
b.send(str(len(l)))
|
||||||
|
sys.exit(0)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.update()
|
||||||
|
gobject.timeout_add(12 * 1000, self.update)
|
||||||
|
if apt:
|
||||||
|
self.debcheck()
|
||||||
|
gobject.timeout_add(900 * 1000, self.debcheck)
|
||||||
|
|
||||||
|
|
||||||
|
def re_exec(*ign):
|
||||||
|
os.execv(sys.argv[0], sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import signal
|
||||||
|
import gobject
|
||||||
|
import dbus.mainloop.glib
|
||||||
|
|
||||||
|
# Set up main loop
|
||||||
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
|
|
||||||
|
# kill -HUP makes this re-exec itself
|
||||||
|
signal.signal(signal.SIGHUP, re_exec)
|
||||||
|
|
||||||
|
# Register ourselves as a service
|
||||||
|
session_bus = dbus.SessionBus()
|
||||||
|
dbus_name = dbus.service.BusName('org.woozle.Status',
|
||||||
|
session_bus)
|
||||||
|
|
||||||
|
# Our founder
|
||||||
|
s = Status(session_bus, '/org/woozle/Status')
|
||||||
|
|
||||||
|
# Go!
|
||||||
|
loop = gobject.MainLoop()
|
||||||
|
s.start()
|
||||||
|
loop.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""watch.py -- Web site change notification tool
|
||||||
|
Author: Neale Pickett <neale@woozle.org>
|
||||||
|
Time-stamp: <2007-09-19 15:00:43 neale>
|
||||||
|
|
||||||
|
Usage: urlwatch [-c|--config WATCHRC]
|
||||||
|
|
||||||
|
This is something you can run from a cron job to notify you of changes
|
||||||
|
to a web site. You just set up a ~/.watchrc file, and run watcher.py
|
||||||
|
from cron. It mails you when a page has changed.
|
||||||
|
|
||||||
|
I use this to check for new software releases on sites that just change
|
||||||
|
web pages; my wife uses it to check pages for classes she's in.
|
||||||
|
|
||||||
|
You'll want a ~/.watchrc that looks something like this:
|
||||||
|
|
||||||
|
to: your.email.address@example.com
|
||||||
|
http://www.example.com/path/to/some/page.html
|
||||||
|
|
||||||
|
The 'to:' line tells watch.py where to send change notification email.
|
||||||
|
You can also specify 'from:' for an address the message should come from
|
||||||
|
(defaults to whatever to: is), and 'host:' for what SMTP server to send
|
||||||
|
the message through (defaults to localhost).
|
||||||
|
|
||||||
|
When watch.py checks a URL for the first time, it will send you a
|
||||||
|
message (so you know it's working) and write some funny characters after
|
||||||
|
the URL in the .watchrc file. This is normal--watch.py uses these
|
||||||
|
characters to remember what the page looked like the last time it
|
||||||
|
checked.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import urllib2 as urllib
|
||||||
|
import sha
|
||||||
|
import smtplib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
host = 'localhost'
|
||||||
|
fromaddr = None
|
||||||
|
toaddr = None
|
||||||
|
|
||||||
|
rc = os.path.expanduser('~/.watchrc')
|
||||||
|
|
||||||
|
def usage(errmsg=None):
|
||||||
|
if errmsg:
|
||||||
|
print "error: %s" % errmsg
|
||||||
|
print
|
||||||
|
|
||||||
|
print globals()['__doc__']
|
||||||
|
|
||||||
|
if errmsg:
|
||||||
|
sys.exit(1)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def myhash(data):
|
||||||
|
return sha.new(data).hexdigest()
|
||||||
|
|
||||||
|
def notify(fromaddr, toaddr, url):
|
||||||
|
msg = """From: URL Watcher <%(from)s>
|
||||||
|
To: %(to)s
|
||||||
|
Subject: %(url)s changed
|
||||||
|
|
||||||
|
%(url)s has changed!
|
||||||
|
""" % {'from': fromaddr,
|
||||||
|
'to': toaddr,
|
||||||
|
'url': url}
|
||||||
|
s = smtplib.SMTP(host)
|
||||||
|
s.sendmail(fromaddr, [toaddr], msg)
|
||||||
|
s.quit()
|
||||||
|
|
||||||
|
def watch(rcfile):
|
||||||
|
global host, fromaddr, toaddr
|
||||||
|
|
||||||
|
f = open(rcfile)
|
||||||
|
outlines = []
|
||||||
|
for line in f.xreadlines():
|
||||||
|
if line[0] == '#':
|
||||||
|
continue
|
||||||
|
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
splits = line.split(' ', 1)
|
||||||
|
url = splits[0]
|
||||||
|
if url == 'from:':
|
||||||
|
fromaddr = splits[1]
|
||||||
|
elif url == 'to:':
|
||||||
|
toaddr = splits[1]
|
||||||
|
if not fromaddr:
|
||||||
|
fromaddr = toaddr
|
||||||
|
elif url == 'mailhost:':
|
||||||
|
host = splits[1]
|
||||||
|
else:
|
||||||
|
if (not fromaddr) or (not toaddr):
|
||||||
|
raise ValueError("must set to: before any urls")
|
||||||
|
page = urllib.urlopen(url).read()
|
||||||
|
ph = myhash(page)
|
||||||
|
|
||||||
|
try:
|
||||||
|
h = splits[1]
|
||||||
|
except IndexError:
|
||||||
|
h = None
|
||||||
|
if h != ph:
|
||||||
|
notify(fromaddr, toaddr, url)
|
||||||
|
line = '%s %s' % (url, ph)
|
||||||
|
outlines.append(line)
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = open(rcfile, 'w')
|
||||||
|
f.write('\n'.join(outlines) + '\n')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import getopt
|
||||||
|
import sys
|
||||||
|
|
||||||
|
opts, args = getopt.getopt(sys.argv[1:], 'c:h', ['config=', 'help'])
|
||||||
|
for k,v in opts:
|
||||||
|
if k in ('-c', '--config'):
|
||||||
|
rc = v
|
||||||
|
elif k in ('-h', '--help'):
|
||||||
|
usage()
|
||||||
|
else:
|
||||||
|
usage("Unknown option: %s" % k)
|
||||||
|
|
||||||
|
watch(rc)
|
|
@ -1,4 +1,4 @@
|
||||||
[[!meta title="xss"]]
|
[[!meta title="xss: X Screensaver Construction Kit"]]
|
||||||
|
|
||||||
[xss](http://woozle.org/~neale/src/xss) is a suite of X screensaver
|
[xss](http://woozle.org/~neale/src/xss) is a suite of X screensaver
|
||||||
utilities. You can use shell scripts to glue the tools together to
|
utilities. You can use shell scripts to glue the tools together to
|
||||||
|
|
Loading…
Reference in New Issue