thirdculture-werc/sites/plan9.stanleylieber.com/mother/mother
2025-08-22 01:50:41 +00:00

504 lines
10 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/rc
# 2022-11-16T18:11:20-05:00
# Mother wants to talk to you.
# Similar to nedmail. Use with 9front or nupas/fs (creates mdir format files on save).
# BONUS: Helper program for use with faces(1): http://plan9.stanleylieber.com/mother/facemother
rfork en
ramfs -p
argv0=$0
if(~ $#editor 0)
editor=hold
if(~ $#pager 0)
pager=cat
mb=mbox
msg=()
sort=-r
fn d{
if(test $1 -le $#rposts && test -d $rposts($1)){
flag +D $1 &&
dposts=($dposts $1) ||
echo !delete $1 failed
}
if not
echo !address
}
fn deldposts{
ndel=()
for(i in $dposts){
if(test $i -le $#rposts && test -d $rposts($i)){
echo delete $mb $rposts($i) >/mail/fs/ctl &&
echo !deleted $i &&
ndel=($ndel $i) ||
echo !delete $i failed
}
}
echo !$#ndel messages deleted
}
fn e{
>/tmp/e &&
eval $editor /tmp/e &&
yn send &&
if(~ $yn y)
/bin/upas/marshal $* </tmp/e
}
fn fakefile{
if(! ~ $#file 0 || ~ $disp file inline){
file='(file,'$"file')'
fake=`{
if(~ $type *gif *GIF)
echo body.gif
if not if(~ $type *jpeg *JPEG *jpg *JPG)
echo body.jpg
if not if(~ $type *png *PNG)
echo body.png
if not if(~ $file *.*)
echo body.^`{echo $file | sed 's/(^.*\.|\)$)//g'}
if not
echo body
}
filepath=`{echo -n /mail/fs/$mb/^$1^/$fake}
size=`{du $1^/$fake}
echo !--- $1 $type $size(1) $file [$filepath]
}
}
fn flag{
if(~ $1 [\+][aDdfrSs] [-][aDdfrSs]){
echo $1 >$rposts($2)^/flags &&
puth $2 ||
echo !address
}
}
fn fmtd{
date=`{read}
switch($date(2)){
case Jan; mo=1
case Feb; mo=2
case Mar; mo=3
case Apr; mo=4
case May; mo=5
case Jun; mo=6
case Jul; mo=7
case Aug; mo=8
case Sep; mo=9
case Oct; mo=10
case Nov; mo=11
case Dec; mo=12
}
switch($date(3)){
case [0-9]
da=0^$date(3)
case *
da=$date(3)
}
switch($date(6)){
case `{date | awk '{print $6;}'}
ti=`{echo $date(4) | awk '{print substr($0,0,5);}'}
case *
ti=$date(6)
}
echo $mo/$da $ti
}
fn geth{
for(i in $*){
flags=`{sed -n 18p $rposts($i)^/info | sed 's/-//g'}
mime=`{
if(~ `{sed -n 7p $rposts($i)^/info} multipart*)
echo H
}
size=`{sed -n 17p $rposts($i)^/info}
date=`{sed -n 5p $rposts($i)^/info | fmtd}
from=`{sed -n 1p $rposts($i)^/info}
subject=`{sed -n 6p $rposts($i)^/info | awk '{print substr($0,0,50);}'}
# Unicode 00a0 divides the message number from the headline.
# Command input ignores everything after the unicode 00a0.
# These lines may be selected and sent to the prompt
# in order to print the indicated message.
echo ' '$"i' '$"mime' '$"flags' '$"size' '$"date' '$"from' '$"subject
}
}
fn getposts{ ls | grep -e '^[0-9]+$' | sort -n $sort }
fn getr{
switch($*){
case ,; echo $posts
case ,*; seq 1 `{echo $* | sed 's/,//g'}
case *,; seq `{echo $* | sed 's/,//g'} $posts($#posts)
case *,*; seq `{echo $* | sed 's/,/ /g'}
case *; echo $*
}
}
fn h{ sed -n $1^p /tmp/h }
fn m{
if(test $1 -le $#rposts && test -f $rposts($1)^/info){
subject=`{sed -n 6p $rposts($1)^/info}
if(! ~ $subject FWD:* Fwd:* fwd:*)
subject=(Fwd: $subject)
e -s $"subject -A $rposts($1)^/raw $*(2-) &&
flag +a $1
}
if not
echo !address
}
fn mb{
mb=$1
if(test -d /mail/box/$user/$mb || test -d $mb){
if(! ~ $mb mbox){
if(~ $mb /mail/fs/*)
mb=`{basename $mb}
if not if(~ $mb /*)
echo open $mb `{basename $mb} >/mail/fs/ctl
if not
echo open /mail/box/$user/$mb $mb >/mail/fs/ctl
mb=`{basename $mb}
}
cd /mail/fs/$mb
dposts=()
y
post=$posts(1)
prompt=$post
}
if not
echo !^$mb does not exist
}
fn printhelp{
echo 'Commands are of the form [<range>] <command> [args]
<range> := <addr> | <addr>','<addr>
<command> :=
a reply to sender and recipients
b print the next ten headers
d mark for deletion
e ... enter message (args passed to upas/marshal)
g/regexp/cmd grep headlines for regexp and run cmd on matches
h print message headline (,h for all)
help print this help message
m ... forward mail to address(es)
mb ... change to specified mailbox
p print the processed message
P print the raw message
q quit
r reply to message
s ... store message in specified mailbox
u remove deletion mark
y synchronize with mail box
" print message in quoted form, suitable for reply
|cmd pipe the processed message to a command
||cmd pipe the raw message to a command
!cmd run a command'
}
fn pp{
if(test $1 -le $#rposts && test -f $rposts($1)^/header){
{ # Avoid stutter by dumping everything into a file first.
cat $rposts($1)^/header
echo
if(test -d $rposts($1)^/1){
parts=`{ls -p $rposts($1) | grep -e '^[0-9]+'}
body=1/body
if(test -f $rposts($1)^/1/1/body)
body=1/1/body
}
if not{
parts=()
body=body
}
type=`{file -m $rposts($1)^/$body}
if(~ $type text/plain)
cat $rposts($1)^/$body
if not if(~ $type text/html){
hcmd=(htmlfmt -l60 -cutf8 -a $rposts($1)^/$body)
echo !/bin/^$"hcmd
eval $hcmd
echo
echo !--- $rposts($1) $type `{du $rposts($1)^/$body | awk '{print $1}'} [file:///mail/fs/$mb/^$rposts($1)^/$body] # plumb to browser
}
if not{
disp=`{sed -n 8p $rposts($1)^/info}
file=`{sed -n 9p $rposts($1)^/info}
fakefile $rposts($1)
}
echo
if(! ~ $#parts 0){
if(! ~ $#parts 1)
parts=$parts(2-)
for(j in $parts){
type=`{file -m $rposts($1)^/$j/body}
disp=`{sed -n 8p $rposts($1)^/$j/info}
file=`{sed -n 9p $rposts($1)^/$j/info}
fakefile $rposts($1)^/$j
}
parts=()
}
} >/tmp/p
eval $pager /tmp/p
go=1
r=$1
post=$1
prompt=$1
flag +s $1
}
if not
echo !address
}
fn P{
if(test $1 -le $#rposts && test -f $rposts($1)^/rawunix){
eval $pager $rposts($1)^/rawunix
go=1
r=$1
post=$1
prompt=$1
flag +s $1
}
if not
echo !address
}
fn puth{
flags=`{sed -n 18p $rposts($1)^/info | sed 's/-//g'}
mime=`{
if(~ `{sed -n 7p $rposts($1)^/info} multipart*)
echo H
}
size=`{sed -n 17p $rposts($1)^/info}
date=`{sed -n 5p $rposts($1)^/info | fmtd}
from=`{sed -n 1p $rposts($1)^/info}
subject=`{sed -n 6p $rposts($1)^/info | awk '{print substr($0,0,50);}'}
{
echo $1
echo c
# REMEMBER: Unicode 00a0 divides the message number from the headline.
echo ' '^$1^' '$"mime' '$"flags' '$"size' '$"date' '$"from' '$"subject
echo .
echo w
echo q
} | sam -d /tmp/h >/dev/null >[2=1]
}
fn r{
if(test $1 -le $#rposts && test -f $rposts($1)^/info){
subject=`{sed -n 6p $rposts($1)^/info}
if(! ~ $subject RE:* Re:* re:*)
subject=(Re: $subject)
e -R $rposts($1) -s $"subject $*(2-) `{sed -n 4p $rposts($1)^/info} &&
flag +a $1
}
if not
echo !address
}
fn s{
if(test $1 -le $#rposts && test -f $rposts($1)^/raw){
if(! test -d /mail/box/$user/$2)
echo create $2 >/mail/fs/ctl
/bin/upas/mbappend $2 $rposts($1)^/raw &&
flag +S $1 &&
echo !saved in $2
}
if not
echo !address
}
fn u{
if(test $1 -le $#rposts && test -d $rposts($1))
flag -D $1 || echo !undelete $1 failed
if not
echo !address
dposts=`{grep -v $1 <{for(j in $dposts){ echo $j }}}
}
fn y{
go=()
r=$post
if(! ~ $#dposts 0){
deldposts
dposts=()
}
if(! ~ $q 1){
rposts=`{getposts}
posts=`{seq 1 $#rposts}
post=$posts(1)
prompt=$post
geth $posts >/tmp/h
if(~ $#msg 0)
echo $#posts messages
}
}
fn yn{
echo
echo -n $* ' (y, n) '
yn=`{read}
switch($yn){
case y n
;
case *
yn
}
}
fn '"' { pager=cat pp $1 | sed 1d | sed 's/^/> /g' | sed 's/^> >/>>/g' }
fn usage{
echo usage: $argv0 [ -b ] [ -d ] [ -f mbox ] [ -p msg ] [ -r ] >[1=2]
exit usage
}
while(~ $1 -*){
switch($1){
case -b; biff=-b
case -d; debug=1
case -f; mb=$2; shift
case -p; msg=$2; shift
case -r; sort=(); shift
case *; usage
}
shift
}
if(! ~ $#* 0)
usage
if(! test -f /mail/fs/ctl)
/bin/upas/fs $biff #>[2]/dev/null
if(! test -d /mail/box/$user/$mb && ! test -d $mb){
echo !^$mb does not exist
exit $mb^' does not exist'
}
mb $mb
if(! ~ $#msg 0){
for(i in `{seq 1 $#rposts})
if(~ $msg $rposts($i))
msg=$posts($i)
pp $msg
}
while(){
echo -n $"prompt': '
# Command input ignores everything after unicode 00a0.
rcmd=`{read | sed 's/[ ].*$//g' | sed 's/^([0-9]+)?(,)?([0-9]+)?/& /g'}
switch($rcmd){
case ,* [0-9]*
r=`{getr $rcmd(1)}
cmd=$rcmd(2-)
if(~ $#cmd 0)
cmd=p
case *
r=$post
cmd=$rcmd
}
switch($cmd){
case a a' '*
for(i in $r)
r $i $cmd(2-) `{sed -n 2,3p $rposts($i)^/info | sort -n | uniq}
post=$r($#r)
prompt=$post
case b
r=`{seq $r(1) `{echo $r(1)^+10|bc}}
if(test $r($#r) -gt $posts($#posts))
r=`{seq $r(1) $posts($#posts)}
if(! ~ $#r 0 && test $r(1) -le $posts($#posts)){
sed -n $r(1)^,$r($#r)^p /tmp/h
post=$r($#r)
prompt=$post
}
if not
echo !address
case d
for(i in $r)
d $i
post=$r($#r)
prompt=$post
case e' '*
e $cmd(2-)
case g/*
regexp=`{echo $cmd | awk -F '/' '{print $2;}'} # BUG: / is stripped from regexp and cmd.
cmd=`{echo $cmd | awk -F '/' '{$1=""; $2=""; print;}'}
r=`{</tmp/h grep -e $"regexp | sed 's/ .*$//g'} # Strip everything after unicode 00a0.
if(~ $#cmd 0)
cmd=p
if(~ $cmd d h p P u '"'){
for(i in $r)
eval $cmd $i
}
if not if(~ $cmd a' '* m' '* r' '* s' '*){
for(i in $r)
eval $cmd(1) $i $cmd(2-)
}
if not
echo !illegal command
post=$r($#r)
prompt=$post
case h
{
for(i in $r)
h $i
} | eval $pager
post=$r($#r)
prompt=$post
case help
printhelp
case m' '*
for(i in $r)
m $i $cmd(2-)
post=$r($#r)
prompt=$post
case mb' '*
mb $cmd(2-)
case p
for(i in $r)
pp $i
case P
for(i in $r)
P $i
case q
q=1 y # BUP STOP
exit ''
case r r' '*
for(i in $r)
r $i $cmd(2-)
post=$r($#r)
prompt=$post
case s' '*
for(i in $r)
s $i $cmd(2-)
post=$r($#r)
prompt=$post
case u
for(i in $r)
u $i
post=$r($#r)
prompt=$post
case y
y
case '"'
for(i in $r)
'"' $i | eval $pager
post=$r($#r)
prompt=$post
case '?'
echo dposts: $dposts
echo rposts: $rposts
echo posts: $posts
echo post: $post
echo r: $r
if(! ~ $#mlpath 0)
echo mdir: $mlpath^/^`{sed -n 19p $r/info}
if(! ~ $#msg 0)
echo msg: $msg
case '|'*
for(i in $r)
pager=cat pp $i | eval `{echo $cmd | sed 's/^.//'}
case '||'*
for(i in $r)
pager=cat P $i | eval `{echo $cmd | sed 's/^..//'}
case '!'*
for(i in $r)
eval `{echo $cmd | sed 's/^.//'}
case *
if(~ $post $posts(1) && ~ $#go 0)
pp $post
if not if(! ~ $post $posts($#posts)){
post=`{echo $post^+1 | bc}
if(test $post -gt $posts($#posts))
post=$posts($#posts)
pp $post
}
}
}