From Newsgroup: alt.os.linux.slackware
Eli the Bearded <*@eli.users.panix.com> writes:
This doesn't work with maildirs. (NB: I am unfamiliar with "BSD
countmail".
I don't think the original countmail worked for Maildir. "frm" seems
like it should, but doesn't work here. mutt -f ~/Mail does. This is what
I'm working off of.
Since I don't use Maildir myself, I had to construct some test
data. This is what I think a Maildir area looks like:
tree ~/Mail
/home/jayjwa/Mail
roLroCroC cur
roLroCroC drafts
roLroCroC new
roe-a-a roLroCroC 1464003587.H805375P95754.gator3018.hostgator.com, S=20238_2,S:2,
roe-a-a roLroCroC 1564003587.H805375P95754.gator3018.hostgator.com, S=20238_2,S:2,
roe-a-a roLroCroC 1664003587.H805375P95754.gator3018.hostgator.com, S=20238_2,S:2,
roe-a-a rooroCroC 9datatest
rooroCroC tmp
5 directories, 4 files
As I understand it, "new" is the place new message land. "cur" is old so
we don't count those. dotfiles are to be ignored. If a file is in
Maildir but does not contain the regex match it's ignored.
This is version 2.1 of the script, which now handles Maildir. If there's
no Mail, Maildir or MAILDIR set, it falls back to mbox as before. If the
user asks specifically for Maildir but it's not there, it's an error.
env MAILDIR=/home/jayjwa/Mail python3 countmail.py
THREE!
Three mail messages!
HaHaHaHaHa!
mv Mail HideMail
env MAILDIR=/home/jayjwa/Mail python3 countmail.py
No mail in maildir /home/jayjwa/Mail/new/. Exiting.
python3 countmail.py
ONE!
One mail message!
HaHaHaHaHa!
#!/usr/bin/python3
## countmail.py
## Rewrite of BSD countmail in basic Python3 to not use external tools
## and correctly handle Al/pine placeholder mails. Cross-platform for
## Linux, SunOS, and probably others with Python3.
## Works with maildir or mbox. If maildir (~/Mail, etc ) is found,
## this script assumes the user prefers it over mbox. maildir is
## settable, but must contain the subdirectory "/new/". If there's
## no maildir, look for mbox in the standard places.
## Ref:
##
https://en.wikipedia.org/wiki/Maildir
##
https://www.systoolsgroup.com/maildir/
##
https://cr.yp.to/proto/maildir.html
##
https://untroubled.org/ezmlm/man/man5/maildir.5.html
##
## $Id: countmail.py,v 2.1 2026/06/02 20:43:28 jayjwa Exp $
import os, sys, re
# ANSI color codes.
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
# Regex to indicate that there is a mail message in the mail file. MUAs
# like Alpine and Pine have a placeholder message that must be excluded
# from the total count because it is not a real mail message.
mailPattern = re.compile( r'^From: *[^Mail System Internal Data]' )
# Initialize mail count accumulator to zero.
count = 0
# Convert a number to its word equivalent. Handles up to 1 trillion.
def integerToWords( num ):
# dict to match an integer to its word value
d = { 0 : 'zero', 1 : 'one', 2 : 'two', 3 : 'three', 4 : 'four',
5 : 'five', 6 : 'six', 7 : 'seven', 8 : 'eight', 9 : 'nine',
10 : 'ten', 11 : 'eleven', 12 : 'twelve', 13 : 'thirteen',
14 : 'fourteen', 15 : 'fifteen', 16 : 'sixteen',
17 : 'seventeen', 18 : 'eighteen', 19 : 'nineteen',
20 : 'twenty', 30 : 'thirty', 40 : 'forty', 50 : 'fifty',
60 : 'sixty', 70 : 'seventy', 80 : 'eighty', 90 : 'ninety'
}
# thousands, millions, billions, trillions
k = 1000
m = k * 1000
b = m * 1000
t = b * 1000
# Shouldn't happen since there's no such thing as negative mail counts.
if num < 0:
return d[ 0 ]
# Likewise
if num > 1000000000000:
# You don't really have that much mail...
sys.exit( f"{RED}You have too much mail!{NC}" )
# Recursively check the number, based on its size. Return the dict
# entry for that integer number.
if ( num < 20 ):
return d[ num ]
if ( num < 100 ):
if num % 10 == 0:
return d[ num ]
else:
return d[ num // 10 * 10 ] + '-' + d[ num % 10 ]
if ( num < k ):
if num % 100 == 0:
return d[ num // 100 ] + ' hundred'
else:
return d[ num // 100 ] + ' hundred and ' + integerToWords(num % 100)
if ( num < m ):
if num % k == 0:
return integerToWords( num // k ) + ' thousand'
else:
return integerToWords( num // k ) + ' thousand, ' + integerToWords( num % k )
if ( num < b ):
if ( num % m ) == 0:
return integerToWords( num // m ) + ' million'
else:
return integerToWords( num // m ) + ' million, ' + integerToWords( num % m )
if ( num < t ):
if ( num % b ) == 0:
return integerToWords( num // b ) + ' billion'
else:
return integerToWords( num // b ) + ' billion, ' + integerToWords( num % b )
if ( num % t == 0 ):
return integerToWords( num // t ) + ' trillion'
else:
return integerToWords( num // t ) + ' trillion, ' + integerToWords( num % t )
## [ maildir code ] --------------------------------------------
# First, check if MAILDIR is set or if ~/{Mail,Maildir} exists.
# If so, we assume this user has moved to maildir format and
# to prefer it over mbox.
# env MAILDIR=/home/you/Mail python3 countmail.py, if mail in ~/Mail
maildir = os.getenv( 'MAILDIR' )
if not maildir:
# ~/Mail is common. Check for this first, then Maildir. The
# base location is HOME so use this instead of LOGNAME.
if os.path.isdir( f"{os.getenv( 'HOME' )}/Mail"):
maildir = f"{os.getenv( 'HOME' )}/Mail"
elif os.path.isdir( f"{os.getenv( 'HOME' )}/Maildir"):
maildir = f"{os.getenv( 'HOME' )}/Maildir"
else:
## [mbox code] ------------------------------------------------
# Next, try to locate the user's mbox mail file. Respect env var,
# then search the file system.
mailfile = os.getenv( 'MAIL' )
if not mailfile:
# Try to construct a path to a mail file. LOGNAME is consistent
# across Linux and SunOS. Do not use "USER". Check popular places
# but don't go crazy digging for it.
if os.path.isfile( f"/var/mail/{os.getenv( 'LOGNAME' )}" ):
mailfile = f"/var/mail/{os.getenv( 'LOGNAME' )}"
elif os.path.isfile( f"/var/spool/mail/{os.getenv( 'LOGNAME' )}" ):
mailfile = f"/var/spool/mail/{os.getenv( 'LOGNAME' )}"
else:
# Still nothing? We can't work with no mail file.
sys.exit( f"{RED}Can't locate a mail file on this system!{NC}" )
# Assume mailPattern is a mail occurance and count it.
with open( mailfile, 'r' ) as mf:
for line in mf:
if re.match( mailPattern, line ):
count += 1
# "One" is singular, use "s" for the rest.
if count == 1:
suffix = ""
else:
suffix = "s"
# This is the original display format for BSD countmail.
# More or less. Print the message and exit.
print( f"{GREEN}{ integerToWords( count ).upper() }!{NC}\n\n{GREEN}{ integerToWords( count ).capitalize() } mail message{suffix}!{NC}" )
print( f"{BLUE}HaHaHaHaHa!{NC}" )
sys.exit()
# At this point, the script is executing in Maildir mode.
# maildir is base, subdir /new/ is where the files live to scan.
# Construct full path.
maildir = maildir + "/new/"
# Walk maildir/new, excluding dotfiles, for files. Each is a
# possible mail message. Not assuming, the script checks with its
# regex before calling it one.
if not os.path.isdir( maildir ):
# The user asked to use Maildir functionality but it's not there.
# Don't go looking for mbox in this case.
sys.exit( f"{RED}No mail in maildir {maildir}. Exiting.{NC}" )
for maildirFile in os.listdir( maildir ):
# Disregard any dotfiles that might be in there.
if not maildirFile.startswith( '.' ):
# Assume mailPattern is a mail occurance and count them.
with open( f"{maildir}{maildirFile}", 'r' ) as mdf:
for line in mdf:
if re.match( mailPattern, line ):
count += 1
# "One" is singular, use "s" for the rest.
if count == 1:
suffix = ""
else:
suffix = "s"
# This is the original display format for BSD countmail. More or less.
print( f"{GREEN}{ integerToWords( count ).upper() }!{NC}\n\n{GREEN}{ integerToWords( count ).capitalize() } mail message{suffix}!{NC}" )
print( f"{BLUE}HaHaHaHaHa!{NC}" )
## EOF
--
PGP Key ID: 781C A3E2 C6ED 70A6 B356 7AF5 B510 542E D460 5CAE
"The Internet should always be the Wild West!"
--- Synchronet 3.22a-Linux NewsLink 1.2