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
|
||||
created over the years.
|
||||
|
||||
[Firebot](firebot) is an IRC automaton (bot) that works as an InfoBot,
|
||||
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.
|
||||
[[!map pages="src/*" show="title"]]
|
||||
|
||||
[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!
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
--------
|
||||
|
||||
|
@ -22,6 +25,12 @@ License
|
|||
|
||||
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
|
||||
------------
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
utilities. You can use shell scripts to glue the tools together to
|
||||
|
|
Loading…
Reference in New Issue