From 6840df22b6feb745ffae95677313877fae2bb84a Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Tue, 18 Nov 2008 21:32:24 -0700 Subject: [PATCH] Remove a blog post and add some source code --- src.mdwn | 24 +- src/firebot.mdwn | 2 +- src/ipqueue.mdwn | 9 + src/ipqueue/ipqueue.tar.gz | Bin 20 -> 16699 bytes src/misc/9p.scm | 265 ++++++++++++++ src/misc/Makefile | 18 + src/misc/deliver.sh | 38 ++ src/misc/dwm-config.h | 102 ++++++ src/misc/frobnitz.ml | 389 ++++++++++++++++++++ src/misc/gourmet.sh | 38 ++ src/misc/lcdutils-0.2-neale.tar.gz | Bin 0 -> 22581 bytes src/misc/mail-expire.sh | 86 +++++ src/misc/spamfairy.py | 190 ++++++++++ src/misc/wbackup.tar.gz | Bin 0 -> 10240 bytes src/misc/zsdump.tar.gz | Bin 0 -> 40960 bytes src/photobob.mdwn | 2 +- src/postscript/.sconsign | 4 + src/postscript/Makefile | 13 + src/postscript/alice.ps | 562 +++++++++++++++++++++++++++++ src/postscript/banner.ps | 111 ++++++ src/postscript/emergency-card.png | Bin 0 -> 27112 bytes src/postscript/emergency-card.ps | 357 ++++++++++++++++++ src/postscript/graph-paper.ps | 32 ++ src/postscript/index.dirlist | 4 + src/postscript/index.html | 82 +++++ src/postscript/index.stp | 8 + src/postscript/logo.ps | 34 ++ src/postscript/page_dimensions.ps | 117 ++++++ src/postscript/resume.ps | 559 ++++++++++++++++++++++++++++ src/postscript/ruled-paper.ps | 95 +++++ src/postscript/skel.ps | 273 ++++++++++++++ src/python.mdwn | 3 + src/python/htmlpp.py | 100 +++++ src/python/kmp.py | 42 +++ src/python/lousycron.py | 68 ++++ src/python/maildir.py | 46 +++ src/python/ndstrunc.py | 54 +++ src/python/robotfindskitten.py | 116 ++++++ src/python/snpplib.py | 538 +++++++++++++++++++++++++++ src/python/spampot.py | 325 +++++++++++++++++ src/python/status.py | 329 +++++++++++++++++ src/python/watch.py | 130 +++++++ src/xss.mdwn | 2 +- 43 files changed, 5141 insertions(+), 26 deletions(-) mode change 120000 => 100644 src/ipqueue/ipqueue.tar.gz create mode 100755 src/misc/9p.scm create mode 100644 src/misc/Makefile create mode 100755 src/misc/deliver.sh create mode 100644 src/misc/dwm-config.h create mode 100644 src/misc/frobnitz.ml create mode 100755 src/misc/gourmet.sh create mode 100644 src/misc/lcdutils-0.2-neale.tar.gz create mode 100755 src/misc/mail-expire.sh create mode 100755 src/misc/spamfairy.py create mode 100644 src/misc/wbackup.tar.gz create mode 100644 src/misc/zsdump.tar.gz create mode 100644 src/postscript/.sconsign create mode 100644 src/postscript/Makefile create mode 100644 src/postscript/alice.ps create mode 100644 src/postscript/banner.ps create mode 100644 src/postscript/emergency-card.png create mode 100644 src/postscript/emergency-card.ps create mode 100644 src/postscript/graph-paper.ps create mode 100644 src/postscript/index.dirlist create mode 100644 src/postscript/index.html create mode 100644 src/postscript/index.stp create mode 100644 src/postscript/logo.ps create mode 100644 src/postscript/page_dimensions.ps create mode 100644 src/postscript/resume.ps create mode 100644 src/postscript/ruled-paper.ps create mode 100644 src/postscript/skel.ps create mode 100644 src/python.mdwn create mode 100755 src/python/htmlpp.py create mode 100755 src/python/kmp.py create mode 100755 src/python/lousycron.py create mode 100644 src/python/maildir.py create mode 100755 src/python/ndstrunc.py create mode 100644 src/python/robotfindskitten.py create mode 100644 src/python/snpplib.py create mode 100755 src/python/spampot.py create mode 100755 src/python/status.py create mode 100755 src/python/watch.py diff --git a/src.mdwn b/src.mdwn index 613d402..aac43d5 100644 --- a/src.mdwn +++ b/src.mdwn @@ -4,28 +4,6 @@ I write software for a living. I like doing it so much that sometimes I even write software just for fun. Here are some of the better hacks I've created over the years. -[Firebot](firebot) is an IRC automaton (bot) that works as an InfoBot, -LinkBot (links multiple channels, even across servers), command bot -(displays the output of programs), web bot (fetches and parses web -pages, returning the results), and more. Everything runs in an -asynchronous select() loop with a very easy-to-use high-level interface. +[[!map pages="src/*" show="title"]] -[Photobob](photobob) is a web-based photo album. There's nothing to -differentiate this from the myriad other web-based photo albums out -there, except that this one doesn't need a database and doesn't write -any meta-information. You just point it at a directory full of -images/movies and it serves 'em up. - -[eguile](eguile) is a text preprocessor using guile scheme. -[escm](escm) is the same thing for gauche. You could use these to build -a web site with a common theme. I used to use them, but now I use a -very simple m4 template. - -The [Universal Boardgame Kit](ubk/) is a kit you can build for under $5 -to use with a number of boardgames. A few boards are included. I -designed the pieces and most of the boards, and hand-coded them all in -PostScript. That makes me feel manly. - -I also have some little [Python](python/), [PostScript](ps-hacks), and -[other](misc) hacks, which do clever things. diff --git a/src/firebot.mdwn b/src/firebot.mdwn index 2a54df2..aa61766 100644 --- a/src/firebot.mdwn +++ b/src/firebot.mdwn @@ -1,4 +1,4 @@ -[[!meta title="Firebot"]] +[[!meta title="Firebot: IRC Automaton"]] FireBot is a winner! diff --git a/src/ipqueue.mdwn b/src/ipqueue.mdwn index 27a563e..5dd7d26 100644 --- a/src/ipqueue.mdwn +++ b/src/ipqueue.mdwn @@ -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 , or try the IRC channel +[#py-ipqueue on irc.oftc.net](irc://irc.oftc.net/py-ipqueue). + Screen Shots ------------ diff --git a/src/ipqueue/ipqueue.tar.gz b/src/ipqueue/ipqueue.tar.gz deleted file mode 120000 index be60321..0000000 --- a/src/ipqueue/ipqueue.tar.gz +++ /dev/null @@ -1 +0,0 @@ -ipqueue-1.4.1.tar.gz \ No newline at end of file diff --git a/src/ipqueue/ipqueue.tar.gz b/src/ipqueue/ipqueue.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..3755e21bd1cbfe406e2ceee5470566752e0c4dad GIT binary patch literal 16699 zcmV(vKhyyC=WP&k25BzkG?`gTbr8N$Yq0^JH*xcJ}Jz)vMPpUpzS( zoSwdV^+dmX@(27>zBGl_Pcmy#`;B$K^*kOV{{9d7ah?00ei&Z;H2FR5|JB*qWA{H8 zoSoM9|LPR$24^o`zkZ@m{=EO+{pWA`d{sEF-{yXvr@1y+qCc5(zItDoBGFSjUuF1w z>8$VRMPBHQ*}810*H)LgekfdNwJVk87gJl7;bCD@Ydk;uo4#4OG<6#yTm~{rcOO=+ z^U~d$(&ZUZ+?e?nvkdQDR&zZm?Iv^f!GCeQM_#UM;q{Tvi(=c;<7f9)r@1fnHm?eu znYH!DyaU#O8cqV{K)Bj03sW-JH`{WB-Cp!x?C;U5-@UuI(eu=JFiiV0sqnC{Ch=wg zF1@d;2bE#5cE4u&gI(KHNEkiO*Bh70$rkP_R{b}W&1HJBvUauAKmN*p{?nOd-p`BW zKZ1O*kM+c-%jRj7SUtYEf2EJWlD)SXPUAA@Y?|6cC#INtO>}6zx8M^JHA2MOvZo(b zro;!!YWrWu;NWxRt1ni6tO}EVG5W_X|J`59-~U{$jZ6Dr{2zQ~>@`g7SN*oAmMi@u z4B%^jM*nUv++i9g?TSx5`Sa&L#s8mQ-+Uflz5he~|JmtaK=J={aCY+Y^yCG_|FhHA zXMg(tKjP;vf6@4Pe|4wdkFG|y!%Ka0_x5spuJPaKYBEx?06#y&?UE~d(Ssk$tR8&- z{Xi*wo^Q5=TdqodbbgEv|NecCAL)06wR(~-$_G-oxLOA1b| zB=T7f2b!k&gH8Gh;$V5)AOx&usf7Ua6y7dS^6U-Q6pnm6IsKPd@0OWxSQjb1YdCpT5E}+O$3nwxG2V7?P`f?wB(s@NigHnm~1Q2UvG*W!F-M2mJ{U);T(_v(Fl5gm8$Y`ePHQm zk|QF@YmaGU=Wz2J>H_i2wkity(#;A}Z1p22qi_sB&_CAtb6)AW$@s8aEzGd!1ING; zWuEs*Yd)=PrXL{KjWu6L8Z6bwNsk^8M}=M3qM+kYY?TY_kxr^jf&Fp9Ymh(8ncvgB z_MIArOloEB<%V0@(Xt&enLYmWQP7=Y$*Q2(o}dQz*jl>}mDYy&MKfh}!GkT1RnrNy7ES7}Yq)Q&ddbts<(k8|OYh-5mm55jMh`x{ZBNbXCP zKh$)SoM?KY3^{wgooP<5mUbR+C3`r}x02bGEDKALnv)iJArE29T%w??kw-b*N;jYaX@IhGTa*R|#)=Idx|vH|>4G03$|6y9 z@J8E`>=8#n{Ode%3sPSe#XCH(Uk!QbUR-gIoUZ_oO%ypnPfKQ}2zNU^vEbr`1tH#t zaO=_q{eyNmkjfw&@+*xLSgvG7nsl`tq{s54&KvVrw#Rcomio~oASfzA*rQS;$f zPzkJAM`b`%As@t4MPcwSSEzV3WW)B5mO$^zl`bD}r^qY(5Bg|u%yCJ=WLN4?OLg?( z81jRi2Q_I$riYb-P)SCfAEtH*Gv|oseMp&zbgzBGp!pq#A-=};e$4eS^||CI*3gZy z`-Hy@MQ__@Ho#1#V*$u1IkKq_V`9-_{_o8?Fq@JVfPS1&T49cFb`lE;C5k zd@e_#AOk@lw68gq7I{+5g^iGTJ|LH60&vYKT|M1GiiTTZH43&s#2Gx2N;0Aeld`xn$*BJ(8{fNOfbpN&Ga(v2h zLh+hJlMDqX=r!&u=(-YZn}=LIC$!gBe^tK;j`4Ily8SrO!>bE@etmT@o{q1tCi>m= ztq!?VPhX5D)7$aeJ9@+mKVDyq-;K|Q^Z_w{(r4s!2r312WMSh3f@2=?;!E%hgbZ)evm8^4jLs*XP*!-$g)!;8L%{ppSH_Y~;a zLU29Bn5AZ)!$Ru}#J!YMjDE^TnHjNW-Z@fJYl}Fit>YuNo)8V93s$*q1pQ(v!hCb! z)I9tkHtm=foE+QA+90-|&K=5nlNYQ83?_OiFi__ZbOQ44ZI$uS<<_w|A>Tpg=NnTg zY_l>;64}uQct$va1w`DdU!V;cymQ4T;VtrtbRCd3JkC_S8U3`q`%^-mBXZ@zGT6nL zBmpR3Gx7QQPk;dCP?=s%%g6!6`?iW0PJ$Y)lP)6mB42w_i~$u%2fGw->x2-7UuecB8K3h^Y4z zO~sv$;0Eo6pjXCO2|N}tH?zR!?6`2w0~{as)h7Wsx>mBHBDhVoJna(WPJPHEw-N~k zeE~6M`|l*I5vv6<&wLxl(Dk;#HNj?=v4yM=1u6hB7$4lcZSb!m(QMqj$}6AB?(jM6 z%b~mYWJ89I0E0s?MhHx{SEIKF=7$^ zq4c=p2_^HR!<(768FryqhO^bA6|G?q+2n^UAkY;ZS7mXO=-9$O%A`4NS8!Xi8dp`{ zl4LpzUYqLjYU^PfY0zsi=9sR@P6F_@fr<$@G2a9aLuanxQ44=j(ES=S^9Y(*b6zx> z6Yw2Vlv5Rl6()Mj;MtUTHms;PF0O^$M_q9*#8T1IHqd0(-gSh*!RrH|WD*WHP-X_Z zJ)oCR4FK-7wW1_)3U6C+{0GH_BXis&0drG%$slUXxNxb2&pBkvg2frhyo1Uy4NrcR zZ93+dEN}5SLLqvfgtD!mc(ST5e9ZRvVBI7E)MQ)oqDr|~R!D7K#$+B4ocSpOu%fKP zI)COR)X}+iTwu6Zrorzq?9LaI1a%M+AvJnovItT= ze!WGIL88k7LsVlb5=7d9yagwQ;LeegFX&PLlt`gPpRz2k;9aN)7=kbxMaSdmgC5T? zf$+iOxz7<{TI4Uih`?(72v#6`)Gr)21q-gOvBkGwy=Z)tHeVN0*fp3CMs1qL_(kM2 zC%d`6clKe&Co+`=;T?^>&MmvEA1J_fLU!qGx`;K$cn9E(Nl>igkX~zo2w$oMSx4kO zabg`06i>5{5dKxULP{_~afev{7|1==O?d5EiV9ql3(=(3SKhNR4^BpvBP@XD25F%7 zHWXf1K$K4MD*}cdHO(H!C8|Blh!5lQ4Ai)kOVFrbgxKH?TI?;XkMxW-@B#0wA>(_> z!%DJ*whhY765?SK?NJ?zYt_v;bA+~?*tXK8&ue_wgG1dT0$cxh2gE}g;ZApQUGqsA(H0_2#8=9ImA~giHECK zbhSm;CW6e0qlJ`1@Bt^Ea;(BGO_3lN@crW@{Xk(_iepo}*lV>3h*Pe=m$kzTa^he_ zz^GOKW1#9w)h-=TPo1;7f;t*N0nDrv06AYR1#sqZMyZ99PR^PnYDBhwGE>_VL++lwH>neHkQoiClw5z4 zb8W3DxKY!j6%3HrA#CgE4na^EwX>uEok>HFfl=si#SDR>pDiNR4uWgzYcI@;knEYl zuEbk0diQcV_rSnU0`R|g2an5NPV)b_gZX3HLR1wDGd=C6&le%pBYyfr{IwboHu z4!IOk-qDk47Nf&Vqz~vq$feUDTQp8ZDq_M5w*}=o)^+5hMbwN7mD+A*Mgv9E5q-x= zLz`)-$knDVyDQ#Zwv7FlJ-+ZcHmN9Aa!uBP%r31wXC}t`eC}dF3?^Vm@$JH8F3d%x zHm(;xT)2&Nlqq1V7{A)HR1AA$1j@95LvtdW@&hjZo`g==PWg@H8@Cb6_V&)xHWzO3 zQPc?aIO^tcbFeP;)S$Ng+7YGgQZ5XXA)^^*Ad2qkxUqz_`IQmC8XC>$^hi!f9Djit z(-awFLUQBB;e-k?s*t4BZ+!{q#ziJ_yxsFtrUa>08RHh_x!y_z*lvP_a-q2^Jp}p1 z9$IKoCy3Ru0P5C+IvFcE_*0I zf}~u7{Ev(1f?wOHrGq7uXew#1{kow-E+sz%;KCod7^(-Aez*4_vp1CS znwXUp+@xE%nJcAKpPGlddmj?T{qqPN1ect8)3dEK#<*V50oXgm`=d}`e6+}ZEX5sa zIL>RWk^N05>UQq9WB^NTYN|NKF{A%WV^27)*;hM4+(~i5sKcwi^ifK;wgI4g3+n&I zi916LI|dY#pHk##jnSp4*hmgfWKu+oxLZqXHTWZ6ux|E6CAG8dBhAn_LJ{1PLu3e* z%SBaizp68E5^}JnsQygX$!2h_!2z&dLli4+arIT#lEQ?Fpd@6r`2RWGQ)7XlrPOlA ze2$%r>~&v{7ZTXHoB|WAI|CF1khT9>B}=a52}o&Wr_ys)K%x|JY+SSm_Zj;KRIkxT z(rsS5Fk2Y<)iU&b+*2(rWH8KfWJMs|ISTVHbSU8p$d3^T(&e~u!u%7x-mdBA0gZc_+JSm`x2{r{sg-7tG%yfD zmH}5bUSGA_Hmzwri@W6Vd+W6j`u8r8pm$WvM{F;&{r4x6OuXHCdM2+@0dg`Us*8M! zG-3OkM+#cT-NFfR7dStGy*ZEJy3Y*A@>qGil?(ZJT^+5>-i2GWJvAqE=gCXL2*AnZsMSf~z?Iw3 z4N_dqmX&Y1o3BjBXy|k;4r7-uOw!Pymqnl$$1s}&yK}_?`g?VKRXI|9@+1=CH!_|P zwSU7Uc@mh`ANd|_PNUZKNRC?4TnEfryiVArPQ@RngSXB3!}@soO9BDld23B?hwa>tl(x??qMHRX{wVw85F>OK!RJ%l821~J%s zF@Txfk5&>@eYszEq$J|+{n%XFty6p#re1eEmh-Lg32Gn2;ZRPD3UXQ@J0)Fdw12j3 zI$P~LU3_!{NA_lk$SZQ{!^I#z$O$DGigrEjYQX$F9yiZVZqcQ@$ArB zEW*yAm9fAW+K;n5*&Zw>f8Xaxm`6v^Nm6n8qOkXlySH-XG}L@AU&8Pz-2EdX>4E{A zd`XXf<9HJ~Tbl+O4=E-hyd&oh-u=cEJhmRoOr8ukydhtYAP#{$X>A?~=sp~)}C6bjkz&ba~7Pf@2uTR zB*ay=2I@kjeM-)o2DOSv;c2hQ;y7f-8I7iR-YTGW0d*V_iygwqR9?iXfzDR(f}1ft zQU`TF9rn)NG&DpA+e0GQX_9U08J0Za{P+sxriZyX^qmYDT{`JU@EB)MTh_r?VQ>C~ zXW_*Ewx>{i=-+iLcn4IArg4v0zHHiw4=T*i^LG&IOgccrIttYN$hMpN4ax6p_n5uD z;jeJyYfDDv6~}|R$mr{d=kPTR3SumGu3^CPpz=w>3yq8~bCa@lv+Wi4QSAg4z~xjj z&Wrb(A^?9Bzv0vQ-i%Pn={vT85PkDUW*ZVXm=H$lSC-;W(rxoqr>krIX?S}(yqbRI z`ycf6+tK;(ZZgu-4nvT$0Ix(U!7mx zUGO0ATTr;VosV{%T85NE`!2Q-x%8kN4Zx zIInk}WaE}uSw$hqJ#NiRub3VS@)CKAjEIbkh_t%RPNN0Wk2ccXE9r!bxzOh6coeZV%HOk>@xpIXp3u26RIU8({Z$>P5UG&;5q8}HdL z`@NtCbW>Yyr?CQ%BEina>tmdQ*U+C9bqhLz6M{>$*EnqKH+!z7k${>OV{+H-b((Kl z_yzI-^e;DX``$i)rFh+hVvW6bEu=i$ zjs<(6*OECo76>wrMA*pr6vjv8a}4;=_8*HJu$feYPNS@0Jt6)NiSLo%9)YkUTrNTk z7S1>T#ySpXz??-IO6({N|2DDG6J~i%{vT0n!5M_rWQ$1iA3oa2i5rg$s7xT8^aUO< zSmb4Mbi82pa*z>up#Qtyt*Vo;fMf}o8c zvV0mQ_YQ2j7Z9FU+QDeWL5`M+ym;}P70J+1v2NmZVn~s)6Il&d%p;6z%w#Ti@H`8+ zev=4H^|LJw=8r@Bo%>aBZ6u)rod!cz;2c(EeB|-HV>yTAod3Y;oqi^B&7FD znQNrXQ(Z+Kl~;+6^ElTQz2ng*CZ-lyKSDVoP7%9QqzqkQ2L)nIwLb+3`;tX_h2ORB z-OP5z-E7 zL(Cu;V>>vp8S+37#FI*EkJN!|$84^@W_F~^hAS7>Gd$+eEmvamt@=lO(6t4BmEUd3 zwixeH0!ql@DRb)46z-sp3z96F_ue!*~k%I%tXGO?bX_WxO6O+o-! z>O&7cky>LCOcyX=!6Yh`kdo6;I$DhQURQhkiw}tmmQ3@|8JUXUP6@A}nxp*QwK1~P zo&@uv&7RzIE5ZZ(m~T3!Y|oBY&FDs#sOAy}oI>KKqU4X!(3+y7e8m2m2V$r0qZI{g z+8wr`qB;~viO~CfcpVO|r-9nz$P>@5bwg|&lyrc$2udhIf~9c)^q&Tvui^C=Sq#E< zjM$+!vc*mX&0wOUR;fW_|AxJ>5B&b8U`{0PB^#2U>o;J@$u(@XaQe!%H$hgLkH!>- z1cesPa4}v-G*TX3?*M~Due*ZmYAuW08zmYJ#2<5gNw;Acey}>^fWBm#o3zNsE82O8 zH`GXND6oW9(OAklocuG9>@%;T%SY}B2pVMDWwvPO)gx_{VhM1iLSmwDrHn+CQ}+M_ zI^{o;6YIjtk9D!E+I)zl^k<0w)NMBQ4x9HK|F5>S6#uDOefALl={`QKqm%BjRmyxU zB}4!x;$V4KbD%OnnEW{keKy~LoB{SIB1YVXT$N@Z`2nAa;q-;vgzyVU7K&y-Zl-Hu zxutG?#~~4A6@y8vgE@8`aR~MU$Iy2`JkjdX-mH`+qbW)(B%ziTv*fni=Yw?WN|U{M zfb6YNL>+&?%Ft1yr6LXxLU(pP@h%l%kJO~>cjj8dAQ-b;rCcjmZaNEM9BFz8^&*J1 zt)t#aquoZm&C(qIMa^v##EV&ZcCDI)dT(3pW^cL5=`GPEUrJ_4~9CAQhM^@xHaQItMDy3g>K>$BsFe`TM%>CgnUIuw^NkgzAHrJ(Bj zK};w@d5Pf(t+@6(V|mO!!^0HXSv~5}wKbmV*>(fPR?8MB-fRI!$~l4W;^lN4M69+D z1Kf}-8tm#eBF$!{yb1rSQn${?1#Es6o>A?_6a-l`G^Qh}Y73p)WP*(HeGZ=(Fn?@W zkFMB|X(T+7^%yL~Ap%njuL)kL#i7J5Qm#96NoGAtp*r2fDbK+x=1M^Yk8p@~XN3DOMzvE08(&%0qjmcbaGw-w4xSu~b^CNSAcI@{2|Hts z`+=lT<=)uGpnF(V5xTnFmGH2zhc5y3C^sQYwu2#0%btgB0-gtLG{of$D^NI8iOk9Y zUQwY-CU!wYx{p+xRe?=t4jbK>23KCI^W!lYN&;vCM=pX98>VrjR|6iwza@4n&}^E@ zO0~54;;vI^^a^_9A!mXP9g2n`i8Jep--r;v_M%T+GQ!rDZ=B!i4Xt(Fd+p<+efkG& zDn{}D|L^}dRvs4tuOvszrADhjZBfbKPvIQAIzVrEX-QA_I++y(oH2sz#u{YK;9zcH zVymS}?XGjHnW`h>hBlRM2Idgh#%vyJ=1mOL^q?f#U}8UEE{@)MwxmS11HT$?@cPlk zO9J?{*FEmI_@#Gp+-@Gx*B?)tr%n3SZFZWClOpkJG9ROAgCLrrYD~unnfU1Ye;$Z6 z8I1YwApfhqeXsI=rS^PN)BkPw4)h<70T1;59zN~8cI$P|-nC0(Kn3f~+je80ezrCi z(R@Q3pf*_6->|IC`;&v?BPc-GXQSyxFufE(*6KA+PCJnI7l(@#^)9r~n6?h0(P%ud zta1l3->aYWZ{2$I7l-N>v!#?&EPu_CT3}XX(Ex91(Z6OnPS&N6Q*o01rm8-gPwdiA zlLR1$>;Gl1!wCA8l?aS=pp^Yj<Pf zigXv~!C~X5^|skNDPrC1RN4_%u>O$!uRDE@`@agu{w=ltpH*wmasO8<+YkHyK0X_o z{fQ^1y}n^VVsdqXtk+%rCIBjhrH%+4rn~Wer;VA`6BWz;C5sZ}i&sY47!LEFH?+zi zn~9_+od!im@P8mr>GWVepN5zOX+!A5kPI4tCtFX*=`>Kk?b`PCvu*1M$fu*Bg`Ee^ zFOGZ>^_ao~^@rj73dh{JKDnL+EHh$J{Iw%xAX75dWhy=BM>yZHEW9tlUPSSH0r|ht z^@=L)9W)zz5{ihjp|%G?&;S>K|L(9q^#x$)`q^T*V|}r;=(>a5#)X=4^>B{;rm`s! z*YgHp-}63v1j1bS*@8R=uda4c)IsYQ@348&aG`OLj&`?-cIg$(N2z4@$tpn%88B^3 z#@xaD=jqSZG06Agz%fm1?s47OhjlFiu%3xs5+s`LVi5v)XafXP9F<6Ogqm&T`YDqk zx(DUUFGq}UhyX~=D&sf&KMqUsAW0oCNXFrPwt#0xVHfv>4xYdD>)*(sc7Y#^o}8>v8VzwJPNU)~1$_%O_K^ zYi~*M(a?4tcdi@rbFbsiqu>Nr!NH!mcHyt1Q5`y8aP6lA7XE4&#Ukv?N4!?{(P_KQ zZG1_Sf+sWWll1pGa*UkCd>jiNyM!QRxv*zc%_s$9o6b!LoBcIo;B5T^;FzP^WfdUlA2|6RLLv1W2O< zEQ3(f;!d)NSmnkwf&9`dAcz3hVd_{!#cEd%SQHYk`zRD_muNfFQX%n91@_BZuXWTsk=}h_=inu% zwciJ|rod*0&Lwj(BgMa50uNt-zc|I*nL%B+qv!L6se-mJR2O8!X2nv{c|2<)vO7l4 zT-#ZAQ2K=z7iF)?s$vT`kkTg33qYYkFi-uefnb$v-#Inu+5mlN!fQ3L=F|y<<*qye zLEklW zw9)2D6T)1vR=}Wt0UY87B$GXQgaet;Bsc}y>o!|-D3PI_Gt`0_-xrw^7*sDL(-?7! zq4<1smmQoF@4+G2=L?QDA&(ZImy@L)4E-dTny?F6qunwEJQ`N>ezKlB6b55%47vWblS;WzB>3g0Vx3N+>}b z1XmtaU(+xnFms|qLNR%3g3JVhv7nd=7CH@;@TVnSR1pL*N>cP%O_FgzCO(|4T@=Mh zC6<7h6nFzKHzNcv@MB+z&QgzokRl%Dk;3MnpIZ`dhoogRdqlu2)K10xQ8J@|2Td%cC`O~aq_Se5h&E3Yu1q_GO382$XNp3~d6-ueot;B0c>srKvIwS> zemIr_ctB3&V}k1Zm4`lsscdmFRN(pGRD=@8ME3A)44xU>F>3DLhl@2d-wq^_*J!}k zy-f2&u@Bp8k8W(ypKKyDQ9ncilnNoy7Dt!0;V2jn$V1JQI6^mX5yGFZ-XMweoU=)o zpqN~Y&6-5#F=;dY=3Ph4lc?VWr1X05i@WEDD9XJM zrbihmu}@KME$_K@rKko_EGz=x9>H(-Q#YZIV4JpxDwC-lszB8UuobRW#S0f%sj={ZW|2@ zgBdRTOT$TzMa|YA*X-B#SZ5+zc)$AHFaJI;RzIcFk{EPZ3do1ZLC#1W}Cl1bG z;aJ3$6Z5t!3WAAvjf|$BSiIy2PiXX^Ty@qJMMoKjhl`f`S4sdPdkl16K^GJfeD825 zD5QH&B1|wKF(gO6MA76?K3{R#ZrgRn?zrfV-3b0Sg8xNN;J;$QMK?+wgkxT{7P-;V zE16`v7QDG6u?M3N>P&Pd`JcxAWac3kxV_Zkreef4j7&LVt88Kw9$E8w2QYmD= z(>T0{9NpNJrG&XSdakdqV!nu4lvMV%2;j2sj=6nDe@GTcRtVpfjrrP&~IvC~L73y%i2R$-o9 zivrU{))|?VREQ{^DdL3hBu8GEkyy0J)%h*=<{_zJ_3b$uF{4l~VP9}cDDtux)=TI~ zVb)?fPC~DG4Me1K+3V`HT)%>d1JoDA8F0)R}(XT)Ai$w(ef*f%nKa~-~&U`*I$ z8f=UP2~U^igcFT^1zfTf&hCg~hs35cZWK~yS`Hj5b)X9s&9_shsT+la_UNzC6V$UJ z`aRY7_mmj>Q@x39*STtBL6xR5$gm!vmQ#=?H-nKWH;a%fH;2*8%=-PxTY+$+PBg6n zQ;3q1hEls%O&jgwC6Pzz1-_BIv&5tELV|Y|Rp6nb!&n%N*sBV;F*;2kGlt@paBbz{ zh-gOYov z>|+-*D_E5Jt4aPV!@|&;NXgsS>Zt!pi;rrR5BbUgtq9K1r24OOS2Qt)Nh4;6&Ga%&f?c7B*T~{T~~hR z)s0qXQJCI&ke4+NX1P`T$dh0&`I$NhRetuwpUqGL$jQC%qYH%_xKf8~GZ4j#;c(TK zQdB5!Tk(}#>Q*s}zuk-*w(d(D6E z9CuF~)G8M(3U|%*Bqfcm_WH!N*9SZLb_v#ngTf}34) z*PJzBUjqWbb-n!{-a`&J(dT{qiL9&@2NPjuKqm|Ovi0crsL7|RJmQ}!R=hJ%yxqM6 zP%L(LpnSHz16VFtR5QF1vPeG(AIXC%g>LPpma;Usd|+JYF5p-Iy9FEo(#ci!{6fNAXQb#Dgq;#M%7#{2ehWAA1#-)l4mzIGJq=ga#SLH`q*I1wMYDGz1-Ktx^RtlMv}vETQDQ#hm&OswMoCSvr+5Gey5$(n?a{Bln^g`8%@j&99hh1aW3L`R^Ql2R%e5j zYa^QXH)hu*g%?HZe+T_HU83*J|LfW2^X*Lhhwa+42l{_c{vYdx6O(1H!+-W`onUzI zb1*#mhF{3w!<86A6g7QBk*n0Bp=e(wqUI-ZdS}oou!I>!eZH8=yL0^L9hqiuD0m9! zz;uFr@Xg{k9>JEife1aR*Wm8fge&(lZ5H=A2yIS5-;G{!cXxtdDA3=T? z^FugOS9b-kj>5S-+M|N!@Wde${T5UbaY4dS)ukQOyU_DlkzI=IDRjHkushvm7fv`B zZnFf9^k;Ty!hcEw;rd(b+8CP@Bd0>aI4n`o+BTq_G{$R%btxz$ISHV@ zGDoJM;(8qBVuyD0%fiR|Je-~l%XHj{;=w3PyW2!`7RjVtM;l@^b|;q|e?Id!Fy}^> zAa69g+!DtgDN-O8yo&;NOx*(7G zO;wwEv12+1;b`#++Z)jXtn=jNj#5#R4ZReFHYTZKAM!tbYRTaqy0(*nX|ETb((SqZ zR0CVOWQr0qbTu{xs|J-GmSOrW65sMT@T%2I2E)S%bH<|!>ZWMn&KAQD)$Q6xv$|>N zl)c$h_ipDwjNJ%vN?f~l-1b@>{OcWeTl=k}M%&x#p?9~kUVf_EhZzd9QRfa3jE~_7H+-cr44*g_K%h71clPhJ|J|mrv*jr3GA%Dq273Yvz7RxKW zL}OqJPGq$eLm+GjXP8uq90 zIGwbZjsW12s51`#M)3#B(*rHbsn@e0O8GLsZoK)SeY~%IndP^=S<#Nns%Yz|*ZPm9 zF3CUb7n|Q#mx}g|-~8a=-2n)qx`LT#n|{*kc89LHeVfDd(o381T-=qG91@h2{jVUC z`xErK8erhyjMoAN^&+Tom?~@!Cou;a#->y|4!J1&_H&GnpfKYrekrCZmdj-=Ex+^U zBbK{ws3=^?wtJ`%V)v^|K?yO30lf(OP?{VLBpW+F94L7by6s{gSfZq~hy@kCNPi#r z7cRyFzCatcwFqYIGd)4n!&~G)j9Wx@x%9`#K`(s|U9W9_M#2pIq{zgMKXY-Kh|6+m zkO(fdNDzrYKwa`@MF9ot>Ci7rF>&lY6m}d2vb$T_EdC#;TIb(#iy-r)y2bVya9|A1 zM^kYPmlGESc9(~G26W_D!Uar3Lk0Ny6Q1KpP#436I23R;(~o3@GRQ-YV<3EmVo#~p zCM~ezWUBTIK_7PNkWA=E@|=u5?x^#K#^iH}>eAA>- zEZQ&OUy*{DVmPpA(H7k0GfWWI?e8X;qd_S-8<0IhERV?%c@5=x8TAAfg-@2}m&D+b zo^}~;h$CTSDO6M1fPC~L(~{w3TD$vQt}P_V1f&Q##s98 z-^KKN?)TA9Ym8xL8!D7z^1KfL7@)z|n1a}1@Yt~>u45G5d@-lk`DCBT&$$!s!w6w& zH`M_vG6Z&-cb3|5s(H%2Zu7L)#7i|-oArXIK8s0;SEq0F_8Ip#v&%4$ zDZZielJONU}vC)Yuo<{*Pb_-iy3T?Jc~O=pYv1Iub3b?Q35RKT1mF+)S^?IEok z+wUrpAgP?J_Ma}BiI!l-NPzj-O#HN<_=gIbx2^qLSyOb6J7^>)nkz@%Q`p7(ohSyT z>7ku5Lvo?`bPcFMrdvuig!=|v?ZhEfu{Cz@VH*V2FXQ&mwuW3+{tO3bLut>4gJ>tM zaKd`zBYC-#u7V!a6zY*eTj0>N+`5E+o;%W`~)K07dTrqI2U%3pASN@fJ?V#TxOY)T(YV?9WUFISk_Ct4g$SmHPg(awE+ISvG=;7@Ym_uLC9yKN&ThhhGcGQMn>S$^RWOmz zz+}AU1n#F0j4Bj0qxj7ToD@%HvMlh;-W3oTaDXg>#SBSU|0097_P^+%5J>sn51G|8mi~__HeoEGc+U>*9od^aeo{|feZaPpYi zuU^^Jts?xWZr9w^Mrd4evv${#_KQ20tVqeCskrcY2>K#5BK3xhWe~-pJS?B5wOg>Y zR$WPB=c)D+!1XnDg`T5F+BJnQCbcQWF2PlgNDGASl@YBAKNv!9KV$55fg=$tS=_%0 z_=Oa9`KG8|9G79FJ-IPT0>+F&RXqXFcRdvAEcW{tv4&Od=-WOWddSIP@AW@}s`AGh7B1IE`v!?y? z^0F~*mKRgBIdlq-4-VSxgM)&L%aK|;Kr6(&i0{Q2H2me%fus)jR-8{cmPJlhHU}qx zX+C+=;f>*#yQHj>6(V4KSW56%pNDa&>bmQEF;e&`4A1??1)Yqg%7QJrT3O(TF1VOm zI?Ba1rR32`rbczMs0JrAqKgiMQG7tI%9-)K_M;%3Zwyo-oMpyH-J5frGWz8^g@e&! zG=|VcX#b!Yj}xQ4hEmh9hzQe0HE| z`A=T<`2)^>H^_gLXWO;x`R{qh=-S{*>dW`2Xr&NQ2VAak{d3%(_)$wDBPV9s9`(1?bjNo5-D?0pS%c($tA$biTmqn7h!9^wQa+Iw< z_`3Q@*U_IAU5-Qxu4azjtxgvr)p#Nm=~j(d@niJPSI^!V@(~t}FT8bwZey(id&)$3Wvx+~vy%YJlg+Du|y#xOF0)GN4;M~m${ygpO z^UrF_Lluu%=*)4pNM1lG6a0tmHa`<(QG`5okCey2w&w67X82( z`;)VQZ+~{}>$>fHe);kR-hf`a_Qao2m`Inw6jQBDm4*PH6XC{CZKz+ixfsmpS{4mK zJn!}Ulcsmv-D`G{=XqYcb#l^fdd;J~R^uogs*cPgF2?n(o#k}#(#5(>5C zutxP&O+G;;Xg!e_355>7syG~dwqKr&;%Ec!rhGK%EvS1cf+Wuu*l{S<2$Zr}qomjf z$fuK4AipGKIQP%Ja5&t_fQ-kP6cs%)3xFa#7>UoSUd*bpY~mnE+*n837;RvPCZ-e` zLQEFE?2tBCsdLnOjt)oh!YFlyTE~Gpz)m~=KUE6YhVE7@d>ikePQv$6yr9%qx( zykyg9Ub2a-Ld+zx%*WONDYfD2*9Qj&@J}0~iQuR|)T@<`i8RK#Dg9UqkL6;FpfY4k zrYNZjvv3|?V4G6OUbnZbw5m)k&n~xJt<<^J$8<6Eu8b zZn^wYf<;S91h``2Nfw!)gq5I9KGLY7309~BkOD3{$g)};dWY(A3%+fsuLjGj)$d9Z z86~|z zkX;?Lbp$!K`l5K(*}P*b!<!JvvNPLBBHlY_Pjm6qELnktRk~1y0D7M;uV^SxEg6!(#zT{1hd9u zvZl!-9Os-?r?_-DSPb;(AR{VYcy#Z&qx(wUiWoS^@VUs;7%bITvUuR2o_CjHTgk)a zW&v{tr}!g=k(Tpo9=S-uM>~b15MAl;pv$WF$A(G|<^|hPqM)AIG%Hb)v?LJAROF=R z@EAxXrk%$wK>ooSa_P>8`is@9LX~-8G7Dbc6?-pQ61ccI>t<<%Cg{1{sxNs_rHNe=Zv9*shQxHoz$FRLn_K}Iu@}}3l@B-7NkwX zt8p+mr`O?(%1>yg-AQ?%56c(c=PI1z(@i*wDLTg$Q^S|yASUF$rPpy3(2{~7xP)B_+oxerhy?n zV|8zA0=D(t*aYfEm)QjDr$2^Gz$VQ}^;0Li&(%C#0VV<2{*0hsPC6`KUQ3HfPAV)= zK09i+=}=y9)!1{jFTCAc4eoO_NBu(mU!{Cu2&8{e1*~i*6IG1r%kA12RdQJ&6VLj$ z>PXga(vdJLSix&)u(>LdoVmJ@L+cf|XEvQi$rXkCn{*FZgk1q6i@z(1rO|m+rx%mk z3n8+nr(8(^36lK*l1Ca}mLw02_74}O|10i)J3s7~T1Rh>@7MpUR@u_`zt6YX|Eu=w z;r{nNK8Hc 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) \ No newline at end of file diff --git a/src/misc/Makefile b/src/misc/Makefile new file mode 100644 index 0000000..5f98566 --- /dev/null +++ b/src/misc/Makefile @@ -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) \ No newline at end of file diff --git a/src/misc/deliver.sh b/src/misc/deliver.sh new file mode 100755 index 0000000..2d20203 --- /dev/null +++ b/src/misc/deliver.sh @@ -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 \ No newline at end of file diff --git a/src/misc/dwm-config.h b/src/misc/dwm-config.h new file mode 100644 index 0000000..4fc2485 --- /dev/null +++ b/src/misc/dwm-config.h @@ -0,0 +1,102 @@ +/* + * Description: Neale's DWM config + * Author: Neale Pickett + * 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 }, +}; diff --git a/src/misc/frobnitz.ml b/src/misc/frobnitz.ml new file mode 100644 index 0000000..cd73b16 --- /dev/null +++ b/src/misc/frobnitz.ml @@ -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 + +*) + +(** 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 -> + () + + diff --git a/src/misc/gourmet.sh b/src/misc/gourmet.sh new file mode 100755 index 0000000..9c506ac --- /dev/null +++ b/src/misc/gourmet.sh @@ -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 diff --git a/src/misc/lcdutils-0.2-neale.tar.gz b/src/misc/lcdutils-0.2-neale.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4bd19fb48fc40df47cfbb3da7fc3c7912ff856cf GIT binary patch literal 22581 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 literal 0 HcmV?d00001 diff --git a/src/misc/mail-expire.sh b/src/misc/mail-expire.sh new file mode 100755 index 0000000..daf8918 --- /dev/null +++ b/src/misc/mail-expire.sh @@ -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() diff --git a/src/misc/spamfairy.py b/src/misc/spamfairy.py new file mode 100755 index 0000000..7ab012e --- /dev/null +++ b/src/misc/spamfairy.py @@ -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) diff --git a/src/misc/wbackup.tar.gz b/src/misc/wbackup.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..39d77795fb4bc29f59cfab9909269382a71ecb3f GIT binary patch literal 10240 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 BQB?o{ literal 0 HcmV?d00001 diff --git a/src/misc/zsdump.tar.gz b/src/misc/zsdump.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..d25db80fb849b730895b25a9bff14129aba97946 GIT binary patch literal 40960 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 literal 0 HcmV?d00001 diff --git a/src/photobob.mdwn b/src/photobob.mdwn index 9cb31d1..2d2c95e 100644 --- a/src/photobob.mdwn +++ b/src/photobob.mdwn @@ -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 diff --git a/src/postscript/.sconsign b/src/postscript/.sconsign new file mode 100644 index 0000000..d60f426 --- /dev/null +++ b/src/postscript/.sconsign @@ -0,0 +1,4 @@ +index.html: - 12dab84c3ad5b0dba380c051471c14d6 - - +index.ptml: 1042003175 - 39a0ed00cb554392574b5955dbdc0af9 - +links.html: - 962d7fd4c69ea67cfeb1d0cc88e892a6 - - +links.ptml: 1041913579 - bd8a21beca5f285bf5e8ba50e86f340f - diff --git a/src/postscript/Makefile b/src/postscript/Makefile new file mode 100644 index 0000000..e5078f4 --- /dev/null +++ b/src/postscript/Makefile @@ -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 > $@ diff --git a/src/postscript/alice.ps b/src/postscript/alice.ps new file mode 100644 index 0000000..e74706a --- /dev/null +++ b/src/postscript/alice.ps @@ -0,0 +1,562 @@ +%!PS-Adobe-2.0 +%%Title: Alice in DIGITALand -- an example application of skel.ps +%%Creator: Neale Pickett +%%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 diff --git a/src/postscript/banner.ps b/src/postscript/banner.ps new file mode 100644 index 0000000..fb987fd --- /dev/null +++ b/src/postscript/banner.ps @@ -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 diff --git a/src/postscript/emergency-card.png b/src/postscript/emergency-card.png new file mode 100644 index 0000000000000000000000000000000000000000..070247bda34e2fe74eca0759b0452474080b6610 GIT binary patch literal 27112 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

?X|VQMx#Ig*i|UH2QM{|} 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 PUpUE1DoRuXjlcgdd<&;r literal 0 HcmV?d00001 diff --git a/src/postscript/emergency-card.ps b/src/postscript/emergency-card.ps new file mode 100644 index 0000000..3e45e18 --- /dev/null +++ b/src/postscript/emergency-card.ps @@ -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: diff --git a/src/postscript/graph-paper.ps b/src/postscript/graph-paper.ps new file mode 100644 index 0000000..9ca374c --- /dev/null +++ b/src/postscript/graph-paper.ps @@ -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 diff --git a/src/postscript/index.dirlist b/src/postscript/index.dirlist new file mode 100644 index 0000000..25e9a93 --- /dev/null +++ b/src/postscript/index.dirlist @@ -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. diff --git a/src/postscript/index.html b/src/postscript/index.html new file mode 100644 index 0000000..e7c4b89 --- /dev/null +++ b/src/postscript/index.html @@ -0,0 +1,82 @@ + + + + + PostScript Hacks + + + + +

+ + Neale Pickett +

+ +
+

PostScript Hacks +

+ +

Here are some little programs that are either too small or too +self-explanatory to deserve their own web pages.

+ +
+
alice.ps (19k)
+
Alice in DIGITALand -- an example application of skel.ps
+
banner.ps (2.1k)
+
Boilerplate for multi-page banners
+
emergency-card.ps (11k)
+
Emergency card
+
graph-paper.ps (1.1k)
+
A sheet of graph paper
+
logo.ps (1.1k)
+
Some functions to make PostScript feel like LOGO
+
page_dimensions.ps (2.1k)
+
Displays dimensions of the page, and where your printer clips.
+
resume.ps (13k)
+
Résumé
+
ruled-paper.ps (2.1k)
+
A sheet of ruled paper
+
skel.ps (5.2k)
+
Skeleton for typesetting in PostScript.
+
+ +
+ + + + \ No newline at end of file diff --git a/src/postscript/index.stp b/src/postscript/index.stp new file mode 100644 index 0000000..9e33e5b --- /dev/null +++ b/src/postscript/index.stp @@ -0,0 +1,8 @@ + + +

Here is some neat stuff that's too small or self-explanatory + to deserve its own web page.

+
+ +
+ diff --git a/src/postscript/logo.ps b/src/postscript/logo.ps new file mode 100644 index 0000000..30275ca --- /dev/null +++ b/src/postscript/logo.ps @@ -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 diff --git a/src/postscript/page_dimensions.ps b/src/postscript/page_dimensions.ps new file mode 100644 index 0000000..394c9c3 --- /dev/null +++ b/src/postscript/page_dimensions.ps @@ -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 diff --git a/src/postscript/resume.ps b/src/postscript/resume.ps new file mode 100644 index 0000000..13a43e8 --- /dev/null +++ b/src/postscript/resume.ps @@ -0,0 +1,559 @@ +%!PS-Adobe-2.0 +%%Title: Résumé +%%Creator: Neale Pickett +%%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 + diff --git a/src/postscript/ruled-paper.ps b/src/postscript/ruled-paper.ps new file mode 100644 index 0000000..c86effa --- /dev/null +++ b/src/postscript/ruled-paper.ps @@ -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 diff --git a/src/postscript/skel.ps b/src/postscript/skel.ps new file mode 100644 index 0000000..6eb25df --- /dev/null +++ b/src/postscript/skel.ps @@ -0,0 +1,273 @@ +%!PS-Adobe-2.0 +%%Title: Skeleton for typesetting in PostScript. +%%Creator: Neale Pickett +%%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 + () word eq { + SetBoldFont + false + } { + () word eq { + SetItalFont + false + } { + () 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 \ No newline at end of file diff --git a/src/python.mdwn b/src/python.mdwn new file mode 100644 index 0000000..7009921 --- /dev/null +++ b/src/python.mdwn @@ -0,0 +1,3 @@ +[[!meta title="Python hacks"]] + +[[!map pages="src/python/*" show="description"]] diff --git a/src/python/htmlpp.py b/src/python/htmlpp.py new file mode 100755 index 0000000..96af002 --- /dev/null +++ b/src/python/htmlpp.py @@ -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 '\nprettyprint punting: %s %s\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 ('%s' + % (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 ''' + + + Bee-yoo-ti-ful source code + + + ''' + print '
'
+    # write colorized version to stdout
+    for item in prettyprint(sys.stdin):
+        sys.stdout.write(item)
+    print '
' + + diff --git a/src/python/kmp.py b/src/python/kmp.py new file mode 100755 index 0000000..221c662 --- /dev/null +++ b/src/python/kmp.py @@ -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 ' + +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 diff --git a/src/python/lousycron.py b/src/python/lousycron.py new file mode 100755 index 0000000..359d579 --- /dev/null +++ b/src/python/lousycron.py @@ -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 . + +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) diff --git a/src/python/maildir.py b/src/python/maildir.py new file mode 100644 index 0000000..cfe7ddf --- /dev/null +++ b/src/python/maildir.py @@ -0,0 +1,46 @@ +# maildir.py -- Maildir utilities +# +# Released into the public domain + +"""Maildir utilities""" + +__author__ = 'Neale Pickett ' + +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 diff --git a/src/python/ndstrunc.py b/src/python/ndstrunc.py new file mode 100755 index 0000000..3d9c350 --- /dev/null +++ b/src/python/ndstrunc.py @@ -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('= 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.' + + + diff --git a/src/python/robotfindskitten.py b/src/python/robotfindskitten.py new file mode 100644 index 0000000..aecf026 --- /dev/null +++ b/src/python/robotfindskitten.py @@ -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 ''' + robotfindskitten: a zen simulation + + + ''' % color() + + print '' + + print '

robotfindskitten

' + + print ''' +

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

+ + ''' + + + print '
'
+
+    screen = []
+    for i in range(height):
+        screen.append([' '] * width)
+
+    for i in range(30):
+        x = whrandom.randrange(width)
+        y = whrandom.randrange(height)
+
+        n = whrandom.randrange(len(symbols))
+        s = symbols[n]
+        del symbols[n]
+        s = cgi.escape(s)
+
+        n = whrandom.randrange(len(nki))
+        t = nki[n]
+        del nki[n]
+        t = cgi.escape(t)
+        t = t.replace('"', '"')
+
+        screen[y][x] = ('%s'
+                        % (color(), t, s))
+    # place kitten!
+    screen[y][x] = '%s' % s
+
+    for row in screen:
+        print ''.join(row)
+
+    print '
' + + print '' + +if __name__ == '__main__': + main() + diff --git a/src/python/snpplib.py b/src/python/snpplib.py new file mode 100644 index 0000000..20f3b79 --- /dev/null +++ b/src/python/snpplib.py @@ -0,0 +1,538 @@ +#! /usr/bin/env python + +# Description: SNPP client class for Python + +'''SNPP client class. + +Author: Neale Pickett + +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 + MESSage + RESEt + SEND + QUIT + HELP + + Level 2 commands accepted: + + DATA + LOGIn [password] + LEVEl + COVErage + HOLDuntil [+/-GMTdifference] + 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() diff --git a/src/python/spampot.py b/src/python/spampot.py new file mode 100755 index 0000000..0ee0cb9 --- /dev/null +++ b/src/python/spampot.py @@ -0,0 +1,325 @@ +#! /usr/bin/env python + +### spampot.py -- Spam honeypot SMTP server +### Copyright (C) 2003 Neale Pikett +### 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() + diff --git a/src/python/status.py b/src/python/status.py new file mode 100755 index 0000000..a2e14e6 --- /dev/null +++ b/src/python/status.py @@ -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() diff --git a/src/python/watch.py b/src/python/watch.py new file mode 100755 index 0000000..3801189 --- /dev/null +++ b/src/python/watch.py @@ -0,0 +1,130 @@ +#!/usr/bin/python + +"""watch.py -- Web site change notification tool +Author: Neale Pickett +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) diff --git a/src/xss.mdwn b/src/xss.mdwn index 2a275f6..57caa47 100644 --- a/src/xss.mdwn +++ b/src/xss.mdwn @@ -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