Remove a blog post and add some source code

This commit is contained in:
Neale Pickett 2008-11-18 21:32:24 -07:00
parent 5d2dbe6b9c
commit 6840df22b6
43 changed files with 5141 additions and 26 deletions

View File

@ -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.

View File

@ -1,4 +1,4 @@
[[!meta title="Firebot"]]
[[!meta title="Firebot: IRC Automaton"]]
FireBot is a winner!

View File

@ -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
------------

View File

@ -1 +0,0 @@
ipqueue-1.4.1.tar.gz

BIN
src/ipqueue/ipqueue.tar.gz Normal file

Binary file not shown.

265
src/misc/9p.scm Executable file
View File

@ -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)

18
src/misc/Makefile Normal file
View File

@ -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)

38
src/misc/deliver.sh Executable file
View File

@ -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

102
src/misc/dwm-config.h Normal file
View File

@ -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 },
};

389
src/misc/frobnitz.ml Normal file
View File

@ -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 ->
()

38
src/misc/gourmet.sh Executable file
View 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.

86
src/misc/mail-expire.sh Executable file
View File

@ -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()

190
src/misc/spamfairy.py Executable file
View File

@ -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)

BIN
src/misc/wbackup.tar.gz Normal file

Binary file not shown.

BIN
src/misc/zsdump.tar.gz Normal file

Binary file not shown.

View File

@ -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

4
src/postscript/.sconsign Normal file
View File

@ -0,0 +1,4 @@
index.html: - 12dab84c3ad5b0dba380c051471c14d6 - -
index.ptml: 1042003175 - 39a0ed00cb554392574b5955dbdc0af9 -
links.html: - 962d7fd4c69ea67cfeb1d0cc88e892a6 - -
links.ptml: 1041913579 - bd8a21beca5f285bf5e8ba50e86f340f -

13
src/postscript/Makefile Normal file
View File

@ -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 > $@

562
src/postscript/alice.ps Normal file
View File

@ -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

111
src/postscript/banner.ps Normal file
View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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.

82
src/postscript/index.html Normal file
View File

@ -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>

8
src/postscript/index.stp Normal file
View File

@ -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 ) ?>

34
src/postscript/logo.ps Normal file
View File

@ -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

View File

@ -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

559
src/postscript/resume.ps Normal file
View File

@ -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

View File

@ -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

273
src/postscript/skel.ps Normal file
View File

@ -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

3
src/python.mdwn Normal file
View File

@ -0,0 +1,3 @@
[[!meta title="Python hacks"]]
[[!map pages="src/python/*" show="description"]]

100
src/python/htmlpp.py Executable file
View File

@ -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>'

42
src/python/kmp.py Executable file
View File

@ -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

68
src/python/lousycron.py Executable file
View File

@ -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)

46
src/python/maildir.py Normal file
View File

@ -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

54
src/python/ndstrunc.py Executable file
View File

@ -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.'

View File

@ -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('"', '&quot;')
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()

538
src/python/snpplib.py Normal file
View File

@ -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()

325
src/python/spampot.py Executable file
View File

@ -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()

329
src/python/status.py Executable file
View File

@ -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()

130
src/python/watch.py Executable file
View File

@ -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)

View File

@ -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