add newt(1): nntp client for use with nntpfs(4)

This commit is contained in:
stanley lieber 2014-04-01 14:34:29 -04:00
parent 5d3d085492
commit 8347075fd9
2 changed files with 378 additions and 0 deletions

246
rc/bin/newt Executable file
View file

@ -0,0 +1,246 @@
#!/bin/rc
# bloated, featureful usenet reader for use with nntpfs
rfork en
ramfs
argv0=$0
if(~ $#editor 0)
editor=hold
group=alt/test
maxposts=30
mnt=/mnt/news
if(~ $#newtname 0)
newtname=newt@dont-email.me
fn enterpost{
{
echo From: $"newtname
echo Newsgroups: `{echo $group | sed 's/\//\./g'}
echo Subject: $"subject
echo
} >/tmp/post
eval $editor /tmp/post
cat /tmp/post >$mnt/$group/post
}
fn f { du -a $* | sed 's/^.* //g' }
fn fmtd{
date=`{cat}
if(! ~ $date(1) [0-9]*)
date=`{nshift $date}
da=$date(1)
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
}
if(! ~ $date(3) `{date | awk '{print $6;}'})
ti=$date(3)
if not
ti=`{echo $date(4) | awk '{print substr($0,0,5);}'}
echo $mo/$da $ti
}
fn geth{
for(i in $*){
from=`{awk -F ' ' '{print $3;}' $i/xover}
if(! ~ $#from 0 && ! ~ $#from 1){
nfrom=`{
for(i in $from){
if(~ $i *@*)
echo $i | sed 's/(<|>)//g'
}
}
if(! ~ $#nfrom 0)
from=$nfrom
}
if(! ~ $#from 0){
date=`{awk -F ' ' '{print $4;}' $i/xover >[2]/dev/null | fmtd}
awk -v date'='$"date -v from'='$from(1) -F ' ' \
'{print " " $1 " " date " " from " " substr($2,0,50);}' $i/xover >[2]/dev/null
}
if not
echo ' '$"i' nil nil nil'
}
}
fn getposts{ ls | grep -e '^[0-9]+$' | sort -n | tail -$maxposts }
fn k{
kmnt=`{echo $mnt | sed 's/\//\\\//g'}
f $mnt/$* |
grep -v -e '\/([0-9]+(\/|$)|post$)' |
sed 's/^'$"kmnt'\// g /g' |
sort
}
fn nshift{ shift; echo $* }
fn printhelp{
echo '[0-9]+ print specified message
b back
e enter message
f jump to first message
g ... go to specified group
h print message headlines
help print this help message
k ... list sub-groups under specified group
l jump to last message
n next
p print message with minimal headers
P print message with full headers
q quit
r reply to message
y synchronize message list with server
" print message in quoted form, suitable for reply
|cmd pipe message body to a command
||cmd pipe raw message to a command
? print debug information'
}
fn printp{
if(test -d $mnt/$group/$1){
grep -e '(^From|^Newsgroups|^Subject|^Date)' $1/header
echo
cat $1/body
}
echo
prompt=$group/$1
}
fn printpp{
if(test -d $mnt/$group/$1){
cat $1/article
}
echo
prompt=$group/$1
}
fn usage{
echo usage: $argv0 '[ -f newsgroup ] [ -m mountpoint ] [ -p maxposts ]' >[1=2]
exit usage
}
while(~ $1 -*){
switch($1){
case -f
group=`{echo $2 | sed 's/\./\//g'}
shift
case -m
mnt=$2
shift
case -p
maxposts=$2
shift
case *
usage
}
shift
}
if(! ~ $#* 0)
usage
prompt=$group
if(! test -d $mnt/$group){
echo !$mnt/$group does not exist >[1=2]
exit
}
builtin cd $mnt/$group
go=()
posts=`{getposts}
geth $posts >/tmp/h
post=$posts(1)
echo $#posts messages
while(){
echo -n $"prompt': '
cmd=`{read}
switch($cmd){
case [0-9]*
post=$cmd(1)
printp $post
case b
if(! ~ $post $posts(1)){
post=`{echo $post^-1 | bc}
printp $post
}
case e
enterpost
case f
post=$posts(1)
printp $post
case g' '*
ngroup=`{nshift $cmd | sed 's/\./\//g'}
if(test -d $mnt/$ngroup){
if(grep -s -e '^[0-9]+$' <{ls -p $mnt/$ngroup}){
group=$ngroup
builtin cd $mnt/$group
go=()
posts=`{getposts}
geth $posts >/tmp/h
post=$posts(1)
prompt=$group
echo $#posts messages
}
if not
echo !$ngroup contains no messages
}
if not
echo !$ngroup does not exist
case h
cat /tmp/h
case help
printhelp
case k
k $group
case k' '*
k `{nshift $cmd | sed 's/\./\//g'}
case l
post=$posts($#posts)
printp $post
case p
printp $post
case p' '*
post=`{nshift $cmd}
printp $post
case P
printpp $post
case P' '*
post=`{nshift $cmd}
printpp $post
case q
exit
case r
if(test -f $mnt/$group/$post/header){
subject='Re: '^`{grep -e '^Subject: ' $mnt/$group/$post/header | sed 's/^Subject: //g'}
enterpost
}
if not
echo !message missing
case y
posts=`{getposts}
geth $posts >/tmp/h
echo $#posts messages
case '"'
printp $post | sed 1d | sed 's/^/> /g' | sed 's/^> >/>>/g'
case '||'*
cmd=`{echo $"cmd | sed 's/^\|\|//g'}
cat $mnt/$group/$post/article | eval $cmd
case '|'*
cmd=`{echo $"cmd | sed 's/^\|//g'}
cat $mnt/$group/$post/body | eval $cmd
case '?'
echo mnt: $mnt
echo group: $group
echo maxposts: $maxposts
echo posts: $posts
echo post: $post
case n *
if(~ $post $posts(1) && ~ $#go 0){
go=1
printp $post
}
if not if(! ~ $post $posts($#posts)){
go=1
post=`{echo $post^+1 | bc}
if(test $post -gt $posts($#posts))
post=$posts($#posts)
printp $post
}
}
}

132
sys/man/1/newt Normal file
View file

@ -0,0 +1,132 @@
.TH NEWT 1
.SH NAME
newt \- network news transport protocol (NNTP) client
.SH SYNOPSIS
.B newt
[
.B -f
.I newsgroup
] [
.B -m
.I mountpoint
] [
.B -p
.I maxposts
]
.SH DESCRIPTION
.I Newt
provides an interactive, text-based interface to NNTP
articles served by
.IR nntpfs (4) .
.PP
There are a number of options:
.TP
.B -f
Load the specified newsgroup. Default is
.B alt.test.
.TP
.B -m
Directory where
.I nntpfs
is mounted. Default is
.B /mnt/news.
.TP
.B -p
Number of posts to display, up to and including the most recent post.
.PP
.I Newt
starts by reading the list of messages in the
.I newsgroup,
printing out the number of messages, and then prompting for commands
from standard input. The prompt itself presents the name of the group
and the message number in the form of a file system path relative to the
.I mountpoint.
.PP
The commands are:
.PP
.TP
.I number
Print message
.I number.
.TP
.B b
Print the previous message.
.TP
.B e
Enter a new message, honoring the environment variable
.I editor.
Default is
.IR hold (1) .
.TP
.B f
Jump to the first message in the group.
.TP
.BI g " newsgroup
Change to the specified
.I newsgroup.
The name of a group may be provided in dotted
(\fIalt.test\fR) or path (\fIalt/test\fR) format.
.TP
.B h
Print the disposition, date, sender and subject line
of all messages in the group. These lines are suitable
for selecting and sending to the prompt, in order to
print messages either singly or in aggregate.
.TP
.B help
Print a summary of the available commands.
.TP
.BI k " [newsgroup]
Without an argument,
.I k
walks the directories under the current group
and prints commands suitable for changing to each
available sub-group. When provided with an argument,
it instead walks the directories under the group specified
by the argument.
.TP
.B l
Jump to the last message in the group.
.TP
.B n, or enter key
Print the next message.
.TP
.B p
Print the current message with minimal headers.
.TP
.B P
Print the raw message with full headers.
.TP
.B q
Quit.
.TP
.B r
Reply to the current message.
.TP
.B y
Synchronize message list with the server.
.TP
.BI | command
Run the
.I command
with the message body as standard input.
.TP
.BI || command
Run the
.I command
with the whole message as standard input.
.TP
\fB"\fP
Print the current message in quoted form, suitable for reply.
.SH SOURCE
.B /rc/bin/newt
.SH "SEE ALSO"
.IR nntpfs (4)
.SH BUGS
The list of available newsgroups offered by a given server
may run to many megabytes in size. This complicates
walking the list over a slow Internet connection, and renders
searching all but infeasible.
.SH HISTORY
.I Newt
first appeared in 9front (April, 2014).