Compare commits

..

No commits in common. 'master' and 'v0.6' have entirely different histories.
master ... v0.6

@ -5,7 +5,7 @@ has waived all copyright and related or neighboring rights to this work.
http://creativecommons.org/publicdomain/zero/1.0/
The files mystrverscmp.c and mymemmem
The files mystrverscmp.c and mymemmem.c and mytimegm.c
are MIT-licensed and were written by Rich Felker.
mpick.c is largely based on code by Leah Neukirchen

@ -1,6 +1,5 @@
CFLAGS?=-g -O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2
override CFLAGS:=-Wall -Wno-switch -Wextra $(CFLAGS)
LDFLAGS?=-fstack-protector-strong
LDLIBS=-lrt
OS := $(shell uname)
@ -26,10 +25,10 @@ SCRIPT = mcolor mcom mless mmkdir mquote museragent
all: $(ALL) museragent
$(ALL) : % : %.o
maddr magrep mdeliver mdirs mexport mflag mflow mgenmid mhdr mpick mscan msed \
mshow msort mthread : blaze822.o mymemmem.o mytimegm.o
maddr magrep mdeliver mdirs mexport mflag mgenmid mhdr minc mlist mpick mscan \
msed mseq mshow msort mthread : seq.o slurp.o mystrverscmp.o
maddr magrep mdeliver mexport mflag mflow mgenmid mhdr mpick mscan msed mshow \
msort mthread : blaze822.o mymemmem.o mytimegm.o
maddr magrep mdeliver mexport mflag mgenmid mhdr mlist mpick mscan msed mseq \
mshow msort mthread : seq.o slurp.o mystrverscmp.o
maddr magrep mflow mhdr mpick mscan mshow : rfc2047.o
magrep mflow mhdr mshow : rfc2045.o
mshow : filter.o safe_u8putstr.o rfc2231.o pipeto.o
@ -51,7 +50,7 @@ clean: FRC
-rm -f $(ALL) *.o museragent
check: FRC all
PATH=$$(pwd):$$PATH prove -v </dev/null
PATH=$$(pwd):$$PATH prove -v
install: FRC all
mkdir -p $(DESTDIR)$(BINDIR) \
@ -69,12 +68,11 @@ install: FRC all
release:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
[ "$$VERSION" = "$$(cat VERSION)" ] && \
git archive --prefix=mblaze-$$VERSION/ -o mblaze-$$VERSION.tar.gz HEAD
sign:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
gpg2 --armor --detach-sign mblaze-$$VERSION.tar.gz && \
gpg --armor --detach-sign mblaze-$$VERSION.tar.gz && \
signify -S -s ~/.signify/mblaze.sec -m mblaze-$$VERSION.tar.gz && \
sed -i '1cuntrusted comment: verify with mblaze.pub' mblaze-$$VERSION.tar.gz.sig

@ -1,49 +1,3 @@
## 1.2 (2021-12-12)
* Caution! The tools mdeliver and mexport were buggy in handling and
generation of trailing empty lines in MBOX-RD. Do not import
mbox files generated by mexport >=1.2 with mdeliver <1.2 if you
require verbatim message delivery.
* mshow: add "-A all" to render all attachments
* msed: match header names case insensitively
* mless: prefer setting LESSKEYIN and using .mlesskey
* mcom: take Delivered-To into account for choosing From address
* Many bug fixes.
## 1.1 (2021-01-14)
* mcom: allow tilde prefixed path for profile's outbox setting
* mcom: detect and report mmime errors
* add contrib/mmailto, a handler for mailto: links
* Bug fixes.
## 1.0 (2020-09-12)
* Caution! Backwards incompatible changes:
* As a message name, `-` now refers to the message on the standard input,
and not the previous message anymore. Use `.-` to refer to the previous
message in a short way.
The tools will print a warning if you use `-` and the standard input
comes from a TTY.
* mpick: use the -F flag to read script files.
* mpick: remove msglist support, use plain mmsg(7) arguments.
* Many mblaze tools now make use of pledge(2) on OpenBSD.
* add contrib/mcount, a tool to count mails
* mrep: use Reply-From configuration to find From header
* Many bug fixes.
## 0.7 (2020-05-15)
* All tools now follow symlinks to mails.
* mdirs: add -a to list all subfolders, ignoring Maildir++ convention.
* mcom: add preview alias for show command.
* mrep/mbnc: allow only one message as argument.
* maddr: add -d to only print display name.
* mthread: add -r to reverse top-level order.
* mlist: print number of matches when message selection is in place.
* mpick: many improvements.
* Many bug fixes.
## 0.6 (2020-01-18)
* mfwd: prefix Subject with "Fwd:".
@ -62,7 +16,7 @@
* New tool msearch to wrap several mail indexers.
* New zsh completion _mblaze.
* mnext/mprev were removed (you can call `mless +`/`mless .-`).
* mnext/mprev were removed (you can call `mless +`/`mless -`).
* The GnuPG tools in contrib/ now use gpg2.
* mshow exits with error if it could not extract all attachments
* mrep: add -noquote to disable quoting the message replied to

@ -118,10 +118,9 @@ AUTHORS
Leah Neukirchen <leah@vuxu.org>
There is a mailing list available at mblaze@googlegroups.com (to
subscribe, send a message to mblaze+subscribe@googlegroups.com); archives
are available at https://inbox.vuxu.org/mblaze/. There also is an IRC
channel #vuxu on irc.libera.chat. Please report security-related bugs
directly to the author.
subscribe, send a message to mblaze+subscribe@googlegroups.com) and an
IRC channel #vuxu on irc.freenode.net. Please report security-related
bugs directly to the author.
LICENSE
mblaze is in the public domain.
@ -131,4 +130,4 @@ LICENSE
http://creativecommons.org/publicdomain/zero/1.0/
Void Linux January 18, 2020 Void Linux
Void Linux January 6, 2018 Void Linux

@ -1 +1 @@
1.2
0.5.1

@ -121,7 +121,7 @@ blaze822_date(char *s) {
tm.tm_isdst = -1;
time_t r = mytimegm(&tm);
time_t r = tm_to_secs(&tm);
return r;
fail:
@ -241,7 +241,7 @@ blaze822_addr(char *s, char **dispo, char **addro)
}
if (*s == '"')
s++;
} else if (*s == '<' || *s == ':') {
} else if (*s == '<') {
c = tok;
s++;
} else {
@ -277,7 +277,8 @@ blaze822_addr(char *s, char **dispo, char **addro)
if (memchr(tok, '@', c - tok))
not_addr = 1; // @ inside "" is never an addr
*tc = 0;
if (tc != ttok)
tc += safe_append_space(ttok, sizeof ttok);
tc += safe_append(ttok, sizeof ttok, tok, c);
} else if (*s == '(') {
char *z = skip_comment(s);
@ -420,19 +421,10 @@ unfold_hdr(char *buf, char *end)
compress_hdr(l, end);
}
static int
is_crlf(char *s, size_t len)
{
char *firsteol = memchr(s, '\n', len);
return firsteol && firsteol > s && firsteol[-1] == '\r';
}
struct message *
blaze822(char *file)
{
int fd;
int crlf;
ssize_t rd;
char *buf;
ssize_t bufalloc;
@ -443,10 +435,7 @@ blaze822(char *file)
if (!mesg)
return 0;
if (strcmp(file, "/dev/stdin") == 0)
fd = dup(0);
else
fd = open(file, O_RDONLY);
fd = open(file, O_RDONLY);
if (fd < 0) {
free(mesg);
return 0;
@ -478,21 +467,15 @@ blaze822(char *file)
close(fd);
return 0;
}
if (used == 0) {
crlf = is_crlf(buf, rd);
}
if (crlf) {
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
end++;
end++;
break;
}
} else {
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
end++;
break;
}
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
end++;
break;
}
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
end++;
end++;
break;
}
used += rd;
@ -520,15 +503,11 @@ blaze822_mem(char *src, size_t len)
if (!mesg)
return 0;
if (is_crlf(src, len)) {
if ((end = mymemmem(src, len, "\r\n\r\n", 4)))
mesg->body = end+4;
if ((end = mymemmem(src, len, "\n\n", 2))) {
mesg->body = end+2;
} else if ((end = mymemmem(src, len, "\r\n\r\n", 4))) {
mesg->body = end+4;
} else {
if ((end = mymemmem(src, len, "\n\n", 2)))
mesg->body = end+2;
}
if (!end) {
end = src + len;
mesg->body = end;
mesg->bodyend = end;
@ -614,11 +593,7 @@ blaze822_file(char *file)
char *buf = 0;
ssize_t rd = 0, n;
int fd;
if (strcmp(file, "/dev/stdin") == 0)
fd = dup(0);
else
fd = open(file, O_RDONLY);
int fd = open(file, O_RDONLY);
if (fd < 0)
return 0;
@ -686,6 +661,55 @@ error:
return 0;
}
struct message *
blaze822_mmap(char *file)
{
int fd = open(file, O_RDONLY);
if (fd < 0)
return 0;
struct stat st;
if (fstat(fd, &st) < 0)
goto error;
size_t len = st.st_size;
struct message *mesg = malloc(sizeof (struct message));
if (!mesg)
goto error;
char *buf = mmap(0, len+1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED) {
free(mesg);
perror("mmap");
goto error;
}
close(fd);
char *end;
if ((end = mymemmem(buf, len, "\n\n", 2))) {
mesg->body = end+2;
} else if ((end = mymemmem(buf, len, "\r\n\r\n", 4))) {
mesg->body = end+4;
} else {
end = buf + len;
mesg->body = end;
}
unfold_hdr(buf, end);
mesg->msg = mesg->bodychunk = buf;
mesg->end = end;
mesg->bodyend = buf + len;
mesg->orig_header = 0;
return mesg;
error:
close(fd);
return 0;
}
size_t
blaze822_headerlen(struct message *mesg)
{

@ -8,23 +8,13 @@
#define PATH_MAX 4096
#endif
// maildir filename suffix: use a semicolon on winnt/ntfs, otherwise a colon
#ifdef __winnt__
#define MAILDIR_COLON ';'
#define MAILDIR_COLON_SPEC_VER ";2"
#define MAILDIR_COLON_SPEC_VER_COMMA ";2,"
#else
#define MAILDIR_COLON ':'
#define MAILDIR_COLON_SPEC_VER ":2"
#define MAILDIR_COLON_SPEC_VER_COMMA ":2,"
#endif
struct message;
// blaze822.c
struct message *blaze822(char *file); // just header
struct message *blaze822_file(char *file); // header + body (read(2))
struct message *blaze822_mmap(char *file); // header + body (mmap(2))
struct message *blaze822_mem(char *buf, size_t len); // header + body
char *blaze822_hdr_(struct message *mesg, const char *hdr, size_t len);
@ -95,7 +85,7 @@ int filter(char *input, size_t inlen, char *cmd, char **outputo, size_t *outleno
// mygmtime.c
time_t mytimegm(const struct tm *tm);
time_t tm_to_secs(const struct tm *tm);
// slurp.c
@ -105,7 +95,7 @@ int slurp(char *filename, char **bufo, off_t *leno);
// safe_u8putstr.c
#include <stdio.h>
void safe_u8putstr(char *s0, size_t l, int oneline, FILE *stream);
void safe_u8putstr(char *s0, size_t l, FILE *stream);
// pipeto.c

@ -12,17 +12,7 @@ struct message {
#define isfws(c) (((unsigned char)(c) == ' ' || (unsigned char)(c) == '\t' || (unsigned char)(c) == '\n' || (unsigned char)(c) == '\r'))
// 7bit-ASCII only lowercase/uppercase
#define lc(c) (((unsigned)(c)-'A') < 26 ? ((c) | 0x20) : (c))
#define uc(c) (((unsigned)(c)-'a') < 26 ? ((c) & 0xdf) : (c))
// dirent type that can be a mail/dir (following symlinks)
#if defined(DT_REG) && defined(DT_LNK) && defined(DT_UNKNOWN)
#define MAIL_DT(t) (t == DT_REG || t == DT_LNK || t == DT_UNKNOWN)
#define DIR_DT(t) (t == DT_DIR || t == DT_UNKNOWN)
#else
#define MAIL_DT(t) (1)
#define DIR_DT(t) (1)
#endif
// ASCII lowercase without alpha check (wrong for "@[\]^_")
#define lc(c) ((c) | 0x20)
void *mymemmem(const void *h0, size_t k, const void *n0, size_t l);

@ -14,7 +14,7 @@ _mblaze_message() {
_files "$expl[@]" && ret=0
fi
curmsg=$(mseq . 2>/dev/null)
curmsg=$(mseq .)
if [[ -z $curmsg || ! -f $curmsg ]]; then
_message 'no current sequence'
return $ret
@ -346,7 +346,7 @@ _mshow() {
- regular \
'-n[only print message numbers]' \
'-h[display only given headers]:header list:_mblaze_colon_separated_headers' \
'-A[multipart/alternative preference]:mime type: ' \
'-A[mixed/alternative preference]:mime type: ' \
'-n[don'\''t update current message link]' \
'(-r)-q[print only header]' \
'(-q)-r[print body raw]' \

@ -5,7 +5,7 @@
echo "Content-Type: $PIPE_CONTENTTYPE"
echo
cat
} | mshow -t - | awk '
} | mshow -t /dev/stdin | awk '
BEGIN { split("", ct) }
/^ [0-9]/ { ct[++n] = $2 }
function prefer(t) { for (i in ct) if (ct[i] == t) exit(64+i) }

@ -1,10 +0,0 @@
#!/bin/sh
# mcount - count mails from sequence on stdin
[ -t 1 ] && stdout=1
awk -v stdout=$stdout '
!/^ *</ { n++ }
!stdout { print }
END { fflush(); print n " mails processed" > "/dev/stderr" }
'

@ -10,18 +10,12 @@ FLAGS=$(maddr -a -h from:to:cc:bcc: "$1" |sort -u |sed 's/^/--recipient=/')
FROM=$(maddr -a -h from "$1" | sed 1q)
[ "$FROM" ] && key="--default-key=$FROM"
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
TMPD=$(mktemp -d -t mencrypt.XXXXXX)
trap "rm -rf '$TMPD'" INT TERM EXIT
awk '/^$/,0' "$1" |
mmime |
$GPG "$key" --armor --encrypt --sign $FLAGS -o "$TMPD/msg.asc" ||
gpg2 "$key" --armor --encrypt --sign $FLAGS -o "$TMPD/msg.asc" ||
exit $?
printf 'Version: 1\n' >"$TMPD/version"

@ -1,11 +1,5 @@
#!/bin/sh -e
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
tmp=$(mktemp -t mgpg.XXXXXX)
trap "rm -f '$tmp'" INT TERM EXIT
@ -20,7 +14,7 @@ n=$(mshow -t "$tmp" | awk -F: '
/: application\/octet-stream/ {if (supported) print $1}')
if [ "$n" ]; then
mshow -O "$tmp" "$n" | $GPG --quiet -d 2>&1 || exit 0
mshow -O "$tmp" "$n" | gpg2 -d 2>&1 || exit 0
exit 64
fi
exit 63

@ -1,58 +0,0 @@
#!/bin/sh
# mmailto mailto:... - mailto: handler spawning mcom in a terminal emulator
tryterm() {
if [ -z "$TERMINAL" ] && command -v "$1" >/dev/null; then
TERMINAL="$*"
fi
}
tryterm x-terminal-emulator
tryterm urxvt
tryterm xterm
if [ -z "$TERMINAL" ]; then
echo 'No terminal emulator found, set $TERMINAL.' 1>&2
exit 1
fi
IFS='
'
exec $TERMINAL -e mcom $(
awk -v url="$1" '
function decode(s) {
hexdigits = "0123456789abcdef"
for (i = 1; i < length(s); i++) {
if (substr(s, i, 3) ~ /%[0-9a-fA-F][0-9a-fA-F]/) {
c = sprintf("%c", (index(hexdigits, tolower(substr(s, i+1, 1)))-1) * 16 + \
index(hexdigits, tolower(substr(s, i+2, 1)))-1)
if (c == "\n") c = " "
s = substr(s, 1, i-1) c substr(s, i+3)
i += 2
}
}
return s
}
BEGIN {
url = decode(url)
sub(/^mailto:/, "", url)
split(url, parts, "?")
to = parts[1]
split(parts[2], fields, "&")
args[1] = to
for (i in fields) {
split(fields[i], kv, "=")
if (kv[1] != "r") {
args[length(args)+1] = "-" kv[1]
args[length(args)+1] = kv[2]
}
}
for (i in args) {
print decode(args[i])
}
}
'
)

@ -1,18 +0,0 @@
#!/bin/sh -e
# mopenall [MSG] - open every attachements in xdg-open
[ "$#" -eq 0 ] && set -- .
f="$(mseq "$1" | sed 1q)"
[ -z "$f" ] && printf 'No message %s.\n' "$1" 1>&2 && exit 1
dir=$(mktemp -d -t menter.XXXXXX)
cd "$dir"
mshow -t "$1"
mshow -B -x "$1" 2>/dev/null
for f in * ; do
xdg-open "$f" &
done
wait
echo rm -r "$dir"
rm -r "$dir"

@ -3,4 +3,4 @@
IFS='
'
exec cat -- $(mseq "${@:-.}" | sed 's/^ *//')
exec cat -- $(mseq "${@:-.}")

@ -1,8 +1,8 @@
#!/bin/sh
# mrecode - recode stdin respecting PIPE_CHARSET into UTF-8
case "$PIPE_CHARSET" in
''|*[Uu][Nn][Kk][Nn][Oo][Ww][Nn]*) exec cat;;
*) exec iconv -f "$PIPE_CHARSET" -t UTF-8;;
esac
if [ -n "$PIPE_CHARSET" ]; then
exec iconv -f "$PIPE_CHARSET" -t UTF-8
else
exec cat
fi

@ -3,12 +3,6 @@
[ -f "$1" ] || exit 1
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
IFS='
'
@ -19,7 +13,7 @@ FROM=$(maddr -a -h from "$1" | sed 1q)
[ "$FROM" ] && key="--default-key=$FROM"
awk '/^$/,0' "$1" | mmime | sed 's/$/ /' >"$TMPD"/content
$GPG $key --armor --detach-sign -o "$TMPD"/signature.asc "$TMPD"/content ||
gpg2 $key --armor --detach-sign -o "$TMPD"/signature.asc "$TMPD"/content ||
exit $?
{

@ -57,12 +57,15 @@ STDOUT.sync = true
ARGV.each { |group|
FileUtils.mkdir_p(File.join(dir, group, "cur"))
FileUtils.mkdir_p(File.join(dir, group, "new"))
FileUtils.mkdir_p(File.join(dir, group, "tmp"))
nntp.write("GROUP #{group}\r\n")
msg = nntp.gets
unless msg =~ /^211 /
STDERR.puts msg
$exit = 69
next
end
@ -75,10 +78,6 @@ ARGV.each { |group|
low = high - LIMIT + 1 if number > LIMIT - 1
low = 1 if low <= 0
FileUtils.mkdir_p(File.join(dir, group, "cur"))
FileUtils.mkdir_p(File.join(dir, group, "new"))
FileUtils.mkdir_p(File.join(dir, group, "tmp"))
have = Dir.entries(File.join(dir, group, "cur")).
map { |f| $1.to_i if f =~ /N=(\d+)/ }.compact
@ -136,5 +135,3 @@ ARGV.each { |group|
puts
}
exit $exit || 0

@ -3,12 +3,6 @@
# Needs gpg2 (for OpenPGP) and openssl (for SMIME).
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
[ "$#" -eq 0 ] && set -- .
mshow -t "$1" | DOS2UNIX='/ $/!s/$/ /' awk -v "msg=$1" '
@ -20,7 +14,7 @@ signed && content && !signature && indent == si+2 { signature = 0+$1; type = $2
function q(a) { gsub("\\47", "\47\\\47\47", a); return "\47"a"\47" }
END {
if (type == "" && plain) { // guess plain text armored signature
exit(system("mshow -r " q(msg) " | '$GPG' --verify"));
exit(system("mshow -r " q(msg) " | gpg2 --verify"));
} else if (type == "") {
print("No signature found.")
exit(100)
@ -28,7 +22,7 @@ END {
exit(system("mshow -r -O " q(msg) " " q(content) \
" | sed $DOS2UNIX | " \
" { mshow -O " q(msg) " " q(signature) \
" | '$GPG' --verify - /dev/fd/3; } 3<&0"))
" | gpg2 --verify - /dev/fd/3; } 3<&0"))
} else if (type == "application/pkcs7-signature") {
exit(system("mshow -r -O " q(msg) " " q(signed) \
" | openssl smime -verify"))

@ -7,10 +7,8 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static int aflag;
static int dflag;
static char defaulthflags[] = "from:sender:reply-to:to:cc:bcc:"
"resent-from:resent-sender:resent-to:resent-cc:resent-bcc:";
static char *hflag = defaulthflags;
@ -57,32 +55,23 @@ addr(char *file)
v = blaze822_chdr(msg, h);
if (v) {
char *disp, *addr;
char ddec[16384];
while ((v = blaze822_addr(v, &disp, &addr))) {
if (disp) {
blaze822_decode_rfc2047(ddec, disp, sizeof ddec - 1, "UTF-8");
ddec[sizeof ddec - 1] = 0;
disp = ddec;
}
char vdec[16384];
blaze822_decode_rfc2047(vdec, v, sizeof vdec - 1, "UTF-8");
vdec[sizeof vdec - 1] = 0;
v = vdec;
while ((v = blaze822_addr(v, &disp, &addr))) {
if (disp && addr && strcmp(disp, addr) == 0)
disp = 0;
if (aflag) {
if (addr)
if (disp && addr) {
if (aflag) {
printf("%s\n", addr);
} else if (dflag) {
if (disp) {
print_quoted(disp);
printf("\n");
}
} else {
if (disp && addr) {
} else {
print_quoted(disp);
printf(" <%s>\n", addr);
} else if (addr) {
printf("%s\n", addr);
}
} else if (addr) {
printf("%s\n", addr);
}
}
}
@ -100,19 +89,16 @@ int
main(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "adh:")) != -1)
while ((c = getopt(argc, argv, "ah:")) != -1)
switch (c) {
case 'a': aflag = 1; break;
case 'd': dflag = 1; break;
case 'h': hflag = optarg; break;
default:
fprintf(stderr,
"Usage: maddr [-ad] [-h headers] [msgs...]\n");
"Usage: maddr [-a] [-h headers] [msgs...]\n");
exit(1);
}
xpledge("stdio rpath", "");
if (argc == optind && isatty(0))
blaze822_loop1(":", addr);
else

@ -7,11 +7,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static int aflag;
static int cflag;
@ -151,7 +149,7 @@ void
magrep(char *file)
{
if (!*header) {
char *flags = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
char *flags = strstr(file, ":2,");
if (flags)
match(file, "flags", flags+3);
return;
@ -220,8 +218,6 @@ usage:
if (!rx)
goto usage;
xpledge("stdio rpath", "");
*rx++ = 0;
int r = regcomp(&pattern, rx, REG_EXTENDED | iflag);
if (r != 0) {

@ -1,4 +1,4 @@
.Dd March 30, 2020
.Dd July 22, 2016
.Dt MADDR 1
.Os
.Sh NAME
@ -6,7 +6,7 @@
.Nd extract mail addresses from messages
.Sh SYNOPSIS
.Nm
.Op Fl ad
.Op Fl a
.Op Fl h Ar headers
.Op Ar msgs\ ...
.Sh DESCRIPTION
@ -36,8 +36,6 @@ The options are as follows:
.Bl -tag -width Ds
.It Fl a
Only print the addr-spec address, not the display name.
.It Fl d
Only print the display name.
.It Fl h Ar headers
Only search the colon-separated list of
.Ar headers

@ -1,4 +1,4 @@
.Dd January 27, 2024
.Dd February 20, 2017
.Dt MBLAZE-PROFILE 5
.Os
.Sh NAME
@ -49,34 +49,11 @@ The fully qualified domain name used for
.Li Message\&-Id\&:
generation in
.Xr mgenmid 1 .
.It Li Maildir\&:
If set,
.Xr mdirs 1
will use this maildir when no directories are supplied.
.It Li Outbox\&:
If set,
.Xr mcom 1
will save messages in this maildir after sending.
.It Li Drafts\&:
If set,
.Xr mcom 1
will create draft messages in this maildir (defaults to Outbox).
.It Li Reply-From\&:
A comma-separated list of display name and mail address pairs,
formatted like this:
.Dl Li Primary Name <myname1@domain1>, Name v.2 <myname2@domain2>, \[dq]Name, My Third\[dq] <myname3@domain3>, ...
The first of these that appears in the
.Li Delivered-To\&: ,
.Li To\&: ,
.Li Cc\&: ,
or
.Li Bcc\&:
header will be used as the
.Li From\&:
address in a reply.
If not set,
.Li Alternate\&-Mailboxes\&:
will be used as a default.
will create draft messages in this maildir,
and save messages there after sending.
.It Li Scan\&-Format\&:
The default format string for
.Xr mscan 1 .

@ -1,4 +1,4 @@
.Dd January 18, 2020
.Dd January 6, 2018
.Dt MBLAZE 7
.Os
.Sh NAME
@ -163,9 +163,6 @@ thread it and look at it interactively:
Or you could list the attachments of the 20 largest messages in your INBOX:
.Dl mlist ~/Maildir/INBOX | msort -S | tail -20 | mshow -t
.Pp
Or delete messages beyond a certain age:
.Dl mlist ~/Maildir/INBOX | mpick -t 'mtime < \&"-365d\&"' | xargs rm
.Pp
Or apply the patches from the current message:
.Dl mshow -O . '*.diff' | patch
.Pp
@ -209,12 +206,10 @@ There is a mailing list available at
.Po
to subscribe, send a message to
.Mt mblaze+subscribe@googlegroups.com
.Pc ;
archives are available at
.Lk https://inbox.vuxu.org/mblaze/ .
There also is an IRC channel
.Pc
and an IRC channel
.Li #vuxu
on irc.libera.chat.
on irc.freenode.net.
Please report security-related bugs directly to the author.
.Sh LICENSE
.Nm

@ -1,4 +1,4 @@
.Dd April 21, 2021
.Dd August 27, 2019
.Dt MCOM 1
.Os
.Sh NAME
@ -6,7 +6,7 @@
.Nm mfwd ,
.Nm mbnc ,
.Nm mrep
.Nd compose, forward, bounce, reply, send messages
.Nd compose, reply, forward, bounce, send messages
.Sh SYNOPSIS
.Nm mcom
.Op Fl Ar header Ar values\ ...
@ -126,16 +126,11 @@ on the draft, and print the result of
Re-edit the draft.
.It Ic d
Delete the draft and quit.
.It Ic p
Preview the draft, using
.Xr mshow 1 .
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev EDITOR , Ev VISUAL
.It Ev EDITOR
Editor used to compose messages.
(Default:
.Xr vi 1 )
.It Ev MBLAZE
Directory containing mblaze configuration files.
(Default:

@ -1,4 +1,4 @@
.Dd February 4, 2021
.Dd July 26, 2016
.Dt MDELIVER 1
.Os
.Sh NAME
@ -35,7 +35,6 @@ an MBOXRD mailbox, split it on
.Dq Li "From "
and deliver each message,
decoding it according to the MBOXRD convention.
In this case,
.Nm
will set the mtime according to the value of
.Sq Li Date\&:
@ -70,7 +69,6 @@ Override the flags of the new message file to be
.Ex -std
.Sh SEE ALSO
.Xr mexport 1 ,
.Xr mrefile 1 ,
.Xr maildir 5 ,
.Xr mbox 5
.Pp

@ -1,4 +1,4 @@
.Dd July 25, 2023
.Dd July 22, 2016
.Dt MDIRS 1
.Os
.Sh NAME
@ -6,7 +6,7 @@
.Nd list maildir folders, recursively
.Sh SYNOPSIS
.Nm
.Op Fl 0a
.Op Fl 0
.Ar dirs\ ...
.Sh DESCRIPTION
.Nm
@ -17,14 +17,6 @@ for maildir
folders and prints them,
separated by newlines.
.Pp
If
.Ar dirs
is not present then use
.Sq Li Maildir\&:
from
.Pa "${MBLAZE:-$HOME/.mblaze}/profile"
.Pq if set .
.Pp
To
.Nm ,
a maildir folder is a directory containing
@ -35,29 +27,16 @@ and
By the maildir++ convention, nested maildir folder
names must begin with
.Sq Li \&. .
.Pq This can be disabled using Fl a .
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl 0
Print folders separated by a NUL character.
.It Fl a
Traverse into all subfolders, without considering the maildir++ name conventions.
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev MBLAZE
Directory containing mblaze configuration.
.Po
Default:
.Pa $HOME/.mblaze
.Pc
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr find 1 ,
.Xr mblaze-profile 5
.Xr find 1
.Sh AUTHORS
.An Leah Neukirchen Aq Mt leah@vuxu.org
.Sh LICENSE

@ -73,22 +73,22 @@ and >1 if an error occurs.
.Rs
.%A N. Freed
.%A N. Borenstein
.%R RFC 2045
.%D November 1996
.%R RFC 2045
.%T Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
.Re
.Rs
.%A N. Freed
.%A N. Borenstein
.%D November 1996
.%B MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text
.%R RFC 2047
.%T MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text
.%D November 1996
.Re
.Rs
.%A P. Resnick (ed.)
.%D October 2008
.%B Internet Message Format
.%R RFC 5322
.%T Internet Message Format
.%D October 2008
.Re
.Sh AUTHORS
.An Leah Neukirchen Aq Mt leah@vuxu.org

@ -17,9 +17,6 @@ by moving them from
to
.Pa cur ,
and adjusting the filenames.
If used non-interactively with no specified folders,
.Nm
reads directory names from the standard input.
.Pp
By default, the new filenames are printed,
separated by newlines.

@ -43,14 +43,6 @@ Use
and
.Sq Ic ":p"
to read the next (resp. previous) message.
.Sh FILES
.Bl -tag -width Ds
.It Pa ${MBLAZE:-$HOME/.mblaze}/mlesskey , Pa $HOME/.mlesskey
Additional keybindings loaded for convenience.
.It Pa ${MBLAZE:-$HOME/.mblaze}/mless , Pa $HOME/.mless
Additional compiled keybindings loaded for convenience.
(Only needed for less earlier than version 590.)
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO

@ -1,4 +1,4 @@
.Dd April 30, 2020
.Dd December 13, 2016
.Dt MLIST 1
.Os
.Sh NAME
@ -77,10 +77,8 @@ Don't print filenames.
Instead, print a one-line summary for each folder,
showing the number of unseen, flagged and total messages,
along with the folder name.
If any of the message selection flags are used,
the number of matching messages is printed as well.
If two or more folders are specified,
a total for all folders will also be printed at the end.
a total for all folders will also be printed.
.El
.Pp
Multiple options are regarded as a conjunction.

@ -46,14 +46,6 @@ Override Content-Type for the toplevel part.
Defaults to
.Sq Li multipart/mixed .
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev MBLAZE_RELAXED_MIME
If set,
.Nm Fl c
will relax the body line length check and only consider bodies with lines
longer than 998 characters need to be MIME formatted.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
@ -88,9 +80,9 @@ longer than 998 characters need to be MIME formatted.
.Re
.Rs
.%A P. Resnick (ed.)
.%D October 2008
.%B Internet Message Format
.%R RFC 5322
.%T Internet Message Format
.%D October 2008
.Re
.Sh AUTHORS
.An Leah Neukirchen Aq Mt leah@vuxu.org

@ -1,4 +1,4 @@
.Dd July 3, 2020
.Dd July 22, 2016
.Dt MMSG 7
.Os
.Sh NAME
@ -77,9 +77,7 @@ with more indentation
.Pc .
.Pp
The following special shortcuts may be used:
.Bl -tag -width 4n
.It Sq Li \&-
refers to the message read directly from the standard input.
.Bl -tag -width 3n
.It Sq Li \&.
refers to the current message.
Additionally, the syntax
@ -93,7 +91,7 @@ refers to the next message
like
.Sq Li \&.+1
.Pc
.It Sq Li \&.-
.It Sq Li \&-
refers to the previous message
.Po
like

@ -1,4 +1,4 @@
.Dd July 30, 2020
.Dd July 27, 2016
.Dt MPICK 1
.Os
.Sh NAME
@ -6,11 +6,10 @@
.Nd advanced message filter
.Sh SYNOPSIS
.Nm
.Op Fl F Ar file
.Op Fl T
.Op Fl v
.Op Fl t Ar test
.Op Ar msgs\ ...
.Op Ar msglist\ ...
.Sh DESCRIPTION
.Nm
prints all matching messages.
@ -24,11 +23,6 @@ will read filenames from the standard input.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl F Ar file
Read expression from
.Ar file
and only show matching messages, see
.Sx TESTS .
.It Fl T
Include whole thread.
.It Fl t Ar test
@ -39,28 +33,75 @@ see
.It Fl v
Print how many messages were tested and picked to standard error.
.El
.Sh MSGLISTS
.Nm
message lists
.Pq msglist
are mostly compatible with
.Xr mailx 1 .
They are message specifications used as shortened
.Sx TESTS ,
and can include:
.Bl -tag -width Ds
.It Ar n
Message number
.Ar n .
.It Ar n Ns Cm \&: Ns Ar m , Ar n Ns Cm \&- Ns Ar m
An inclusive range of message numbers between
.Ar n
and
.Ar m .
.It Ar address
All messages from
.Ar address .
.It Cm \&/ Ns Ar string
All messages with
.Ar string
in the subject line
.Pq case ignored .
.It Cm \&: Ns Ar c
All messages of type
.Ar c ,
where
.Ar c
shall be one of:
.Bl -tag -width Ds
.It Cm D
Draft messages.
.It Cm P
Passed
.Pq resent, forwarded or bounced
messages.
.It Cm R
Replied messages.
.It Cm F
Flagged messages.
.It Cm d , Cm T
Deleted messages.
.It Cm n
New messages.
.It Cm o
Old messages.
.It Cm r , Cm S
Read messages.
.It Cm u
Unread messages.
.El
.El
.Sh TESTS
.Nm
tests are given by the following EBNF:
.Bd -literal
<expr> ::= <expr> ? <expr> : <expr> -- ternary operator
| <expr> || <expr> -- disjunction
<expr> ::= <expr> || <expr> -- disjunction
| <expr> && <expr> -- conjunction
| ! <expr> -- negation
| ( <expr> )
| <expr> "|" <str> -- pipe current mail to command
| <expr> ">>" <str> -- append current mail to file
| <expr> ">" <str> -- write current mail to file
| <flagprop>
| <timeprop> <numop> <dur>
| <numprop> <numop> <num>
| <hdrprop> <decodeop> <strop> <str>
| <strprop> <strop> <str>
| prune -- do not match further messages in thread
| print -- always true value
| skip -- always false value
| <let>
| <ident>
<flagprop> ::= child | draft | flagged | info | new | parent | passed
| replied | seen | selected | trashed
@ -91,13 +132,8 @@ tests are given by the following EBNF:
| T )? -- *1024*1024*1024*1024
| cur -- index of cur message
<hdrprop> ::= from | to | subject | <str>
<decodeop> ::= . addr -- match address parts
| . disp -- match address display parts
| -- empty matches raw headers
<strprop> ::= path
<strprop> ::= from | subject | to
| <str> -- header name
<strop> ::= == | = | != -- string (in)equality
| === | !=== -- case insensitive string (in)equality
@ -108,15 +144,6 @@ tests are given by the following EBNF:
<str> ::= " ([^"] | "")+ " -- use "" for a single " inside "
| $[A-Za-z0-9_]+ -- environment variable
-- let expressions evaluate the expression following the `in` keyword,
-- the bindings are lazily evaluated.
<let> ::= { let <ident> = <scope> } in <scope>
-- Inside the scope previously defined idents are replaced with expressions
<scope> ::= <expr>
<ident> ::= [A-Za-z_][A-Za-z0-9_]+
.Ed
.Sh EXIT STATUS
.Ex -std

@ -1,4 +1,4 @@
.Dd January 17, 2021
.Dd August 24, 2018
.Dt MSHOW 1
.Os
.Sh NAME
@ -47,25 +47,16 @@ instead of the default headers
.Sq Li from\&:subject\&:to\&:cc\&:date\&:reply\&-to\&: .
.It Fl A Ar mimetypes
Define
.Sq Li "multipart/alternative"
.Sq Li "mixed/alternative"
preference.
.Ar mimetypes
is a colon-separated list of
MIME types which will be preferred,
in the order given,
when rendering
.Sq Li "multipart/alternative"
.Sq Li "mixed/alternative"
parts.
If no MIME type matches, the last MIME part will be rendered.
.Pp
When
.Ar mimetypes
is
.Sq Li all ,
.Nm
will render all parts of a
.Sq Li "multipart/alternative"
part.
If no MIME type matches, the first MIME part will be rendered.
.Pp
Defaults to
.Sq Li "text/plain:text/html" .

@ -1,4 +1,4 @@
.Dd May 4, 2020
.Dd July 22, 2016
.Dt MTHREAD 1
.Os
.Sh NAME
@ -6,7 +6,7 @@
.Nd arrange messages into discussions
.Sh SYNOPSIS
.Nm
.Op Fl vpr
.Op Fl v
.Op Fl S Ar msg
.Op Ar msgs\ ...
.Sh DESCRIPTION
@ -33,12 +33,6 @@ The options are as follows:
.Bl -tag -width Ds
.It Fl v
Do not prune unresolved Message-IDs at the top-level.
.It Fl p
With
.Fl S ,
only add parents, not unrelated subthreads.
.It Fl r
Sort the top-level threads in reverse order (newest threads first).
.It Fl S Ar msg
Treat
.Ar msg

@ -7,19 +7,15 @@ function so(s) { return sprintf("\033[1m%s\033[0m", s) }
BEGIN { hdr = 1; if ("NO_COLOR" in ENVIRON || match(ENVIRON["TERM"], "^(dumb|network|9term)")) no_color = 1 }
no_color { print; next }
/\r$/ { sub(/\r$/, "") }
/^\014$/ { nextmail = 1; print(fg(co("FF",232), $0)); next }
/^$/ { hdr = 0; diff = 0 }
/^\014$/ { nextmail = 1; next }
/^$/ { hdr = 0 }
/^-- $/ { ftr = 1 }
/^diff -/ { diff = 1 }
/^--- .* ---/ { print fg(co("SEP",242), $0); ftr = 0; sig = 0; next }
/^-----BEGIN .* SIGNATURE-----/ { sig = 1 }
nextmail && /^From:/ { hdr = 1 }
hdr && /^From:/ { print so(fg(co("FROM",119), $0)); next }
hdr { print fg(co("HEADER",120), $0); next }
ftr { print fg(co("FOOTER",244), $0); next }
diff && /^-/ { print fg(co("DIFF_D",160), $0); next }
diff && /^\+/ { print fg(co("DIFF_I",40), $0); next }
diff && /^@/ { print fg(co("DIFF_R",226), $0); next }
/^-----BEGIN .* MESSAGE-----/ ||
/^-----END .* SIGNATURE-----/ { print fg(co("SIG",244), $0); sig = 0; next }
sig { print fg(co("SIG",244), $0); next }

109
mcom

@ -15,12 +15,6 @@ notmine() {
grep -Fv -e "$mine"
}
replyfrom() {
addrs="$(maddr -a -h reply-from: "$MBLAZE/profile")"
[ -z "$addrs" ] && addrs="$(maddr -a -h alternate-mailboxes: "$MBLAZE/profile")"
grep -F -e "$addrs"
}
ouniq() {
awk '!seen[$0]++'
}
@ -75,7 +69,7 @@ stripempty() {
needs_multipart() {
mhdr -h attach "$1" >/dev/null ||
grep -qE '^#[a-zA-Z]+/[a-zA-Z0-9+.;=#-]+ ' "$1"
grep -q '^#[^ ]*/[^ ]* ' "$1"
}
do_mime() {
@ -92,10 +86,6 @@ do_mime() {
else
mmime -r <"$draft" >"$draftmime"
fi
if [ $? -ne 0 ]; then
printf 'mcom: fix above errors before continuing\n' 1>&2
rm -f "$draftmime"
fi
}
MBLAZE=${MBLAZE:-$HOME/.mblaze}
@ -233,12 +223,10 @@ case "$0" in
;;
esac
hdrs="$(printf '%s\n' "${hdrs#$NL}" | mhdr -)"
hdrs="$(printf '%s\n' "${hdrs#$NL}" | mhdr /dev/stdin)"
outbox=$(mhdr -h outbox "$MBLAZE/profile" | sed "s:^~/:$HOME/:")
draftbox=$(mhdr -h drafts "$MBLAZE/profile" | sed "s:^~/:$HOME/:")
draftbox="${draftbox:-$outbox}"
if [ -z "$draftbox" ]; then
outbox=$(mhdr -h outbox "$MBLAZE/profile")
if [ -z "$outbox" ]; then
if [ -z "$resume" ]; then
i=0
while [ -f "snd.$i" ]; do
@ -251,13 +239,13 @@ if [ -z "$draftbox" ]; then
draftmime="$draft.mime"
else
if [ -z "$resume" ]; then
draft="$(true | mdeliver -v -c -XD "$draftbox")"
draft="$(true | mdeliver -v -c -XD "$outbox")"
if [ -z "$draft" ]; then
printf '%s\n' "$0: failed to create draft in outbox $draftbox." 1>&2
printf '%s\n' "$0: failed to create draft in outbox $outbox." 1>&2
exit 1
fi
elif [ -z "$draft" ]; then
draft=$(mlist -D "$draftbox" | msort -r -M | sed 1q)
draft=$(mlist -D "$outbox" | msort -r -M | sed 1q)
fi
draftmime="$(printf '%s\n' "$draft" | sed 's,\(.*\)/cur/,\1/tmp/mime-,')"
fi
@ -272,37 +260,37 @@ fi
case "$0" in
*mcom*)
{
printf '%s' "$hdrs" | mhdr -M -h to - |
printf '%s' "$hdrs" | mhdr -M -h to /dev/stdin |
commajoin | sed 's/^/To: /'
printf '%s' "$hdrs" | mhdr -M -h cc - |
printf '%s' "$hdrs" | mhdr -M -h cc /dev/stdin |
commajoin | sed 's/^/Cc: /'
printf '%s' "$hdrs" | mhdr -M -h bcc - |
printf '%s' "$hdrs" | mhdr -M -h bcc /dev/stdin |
commajoin | sed 's/^/Bcc: /'
printf '%s\n' "$hdrs" | awk '{ print }' |
msed "/to/d; /cc/d; /bcc/d; /body/d" -
} | msed "/cc/a//; /bcc/a//; /subject/a//; /from/a/$default_from/" - | sed '/^$/d'
msed "/to/d; /cc/d; /bcc/d; /body/d" /dev/stdin
} | msed "/cc/a//; /bcc/a//; /subject/a//; /from/a/$default_from/" /dev/stdin | sed '/^$/d'
msgid
museragent
cat "$MBLAZE/headers" 2>/dev/null
printf '\n'
(
IFS=$NL
cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body -)
cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body /dev/stdin)
)
printf '\n'
;;
*mfwd*)
{
printf '%s' "$hdrs" | mhdr -M -h to - |
printf '%s' "$hdrs" | mhdr -M -h to /dev/stdin |
commajoin | sed 's/^/To: /'
printf '%s' "$hdrs" | mhdr -M -h cc - |
printf '%s' "$hdrs" | mhdr -M -h cc /dev/stdin |
commajoin | sed 's/^/Cc: /'
printf '%s' "$hdrs" | mhdr -M -h bcc - |
printf '%s' "$hdrs" | mhdr -M -h bcc /dev/stdin |
commajoin | sed 's/^/Bcc: /'
COLUMNS=10000 mscan -f 'Subject: Fwd: [%f] %s' "$@" 2>/dev/null | sed 1q
printf '%s\n' "$hdrs" | awk '{ print }' |
msed "/to/d; /cc/d; /bcc/d" -
} | msed "/cc/a//; /bcc/a//; /from/a/$default_from/" - | sed '/^$/d'
msed "/to/d; /cc/d; /bcc/d" /dev/stdin
} | msed "/cc/a//; /bcc/a//; /from/a/$default_from/" /dev/stdin | sed '/^$/d'
msgid
museragent
cat "$MBLAZE/headers" 2>/dev/null
@ -314,7 +302,7 @@ fi
IFS=$NL
for f in $(mseq -r "$@"); do
printf '%s Forwarded message from %s %s\n\n' \
$SEP "$(mhdr -d -A -h from "$f")" $SEP
$SEP "$(mhdr -d -h from "$f")" $SEP
DISPLAY= mshow -n -N "$f" </dev/null |
sed 's/^-/- &/' # RFC 934
printf '\n%s %s %s\n\n' \
@ -323,47 +311,29 @@ fi
) fi
;;
*mbnc*)
old_ifs="$IFS"
IFS=$NL
set -- $(mseq -r -- "$@")
IFS="$old_ifs"
if [ "$#" -ne 1 ]; then
printf 'mbnc: needs exactly one mail to bounce\n' 1>&2
exit 1
fi
{
printf '%s' "$hdrs" | mhdr -M -h resent-to - |
printf '%s' "$hdrs" | mhdr -M -h resent-to /dev/stdin |
commajoin | sed 's/^/Resent-To: /'
printf '%s' "$hdrs" | mhdr -M -h resent-cc - |
printf '%s' "$hdrs" | mhdr -M -h resent-cc /dev/stdin |
commajoin | sed 's/^/Resent-Cc: /'
printf '%s\n' "$hdrs" | awk '{ print }' |
msed "/resent-to/d; /resent-cc/d" -
msed "/resent-to/d; /resent-cc/d" /dev/stdin
} |
msed "/resent-to/a//; /resent-from/a/$default_from/" - | sed '/^$/d'
msed "/resent-to/a//; /resent-from/a/$default_from/" /dev/stdin | sed '/^$/d'
msgid | sed 's/^/Resent-/'
printf 'Resent-Date: %s\n' "$(mdate)"
(
IFS=$NL
cat $(mseq -r -- "$@")
cat $(mseq "${@:-.}")
)
;;
*mrep*)
old_ifs="$IFS"
IFS=$NL
set -- $(mseq -r -- "$@")
IFS="$old_ifs"
if [ "$#" -ne 1 ]; then
printf 'mrep: needs exactly one mail to reply to\n' 1>&2
exit 1
fi
ng=$(mhdr -h newsgroups "$1")
if [ "$ng" ]; then
printf 'Newsgroups: %s\n' "$ng"
else
to=$(mhdr -d -h reply-to "$1")
[ -z "$to" ] && to=$(mhdr -d -A -h from "$1")
[ -z "$to" ] && to=$(mhdr -d -h from "$1")
printf 'To: %s\n' "$to"
printf 'Cc: %s\n' \
"$(mhdr -d -A -h to:cc: "$1" |
@ -371,17 +341,11 @@ fi
ouniq |commajoin)"
printf 'Bcc: \n'
printf '%s\n' "$hdrs" | awk '{ print }' |
msed "/body/d" -
msed "/body/d" /dev/stdin
fi | sed '/^$/d'
printf 'Subject: Re: %s\n' "$(COLUMNS=10000 mscan -f '%S' "$1")"
if ! printf '%s\n' "$hdrs" | awk '{ print }' |
mhdr -h from: - >/dev/null; then
addr=$(maddr -a -h delivered-to:to:cc:bcc: "$1" | replyfrom | head -n1)
[ -n "$addr" ] && from=$(maddr -h reply-from "$MBLAZE/profile" | grep -Fi "<$addr>" | head -n1)
[ -n "$addr" ] && [ -z "$from" ] && from=$(maddr -h alternate-mailboxes "$MBLAZE/profile" | grep -Fi "<$addr>" | head -n1)
[ -z "$from" ] && from=$(mhdr -h local-mailbox "$MBLAZE/profile")
[ -n "$from" ] && printf 'From: %s\n' "$from"
fi
from=$(mhdr -h local-mailbox "$MBLAZE/profile")
[ "$from" ] && printf 'From: %s\n' "$from"
mid=$(mhdr -h message-id "$1")
if [ "$mid" ]; then
printf 'References:'
@ -402,7 +366,7 @@ fi
fi
(
IFS=$NL
cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body -)
cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body /dev/stdin)
)
printf '\n'
;;
@ -448,7 +412,7 @@ while :; do
if $sendmail <"$draftmime"; then
if [ "$outbox" ]; then
mv "$draftmime" "$draft"
mrefile "$(mflag -d -S "$draft")" "$outbox"
mflag -d -S "$draft"
else
rm "$draft" "$draftmime"
fi
@ -462,11 +426,11 @@ while :; do
continue
fi
else
if mmime -c <"$draft" && ! [ "$automime" = 1 ]; then
if mmime -c <"$draft"; then
stampdate "$draft"
if $sendmail <"$draft"; then
if [ "$outbox" ]; then
mrefile "$(mflag -d -S "$draft")" "$outbox"
mflag -d -S "$draft"
else
rm "$draft"
fi
@ -486,9 +450,6 @@ while :; do
*mbnc*) mflag -P -- "$1" ;;
*mfwd*) mflag -P -- "$@" ;;
esac
case "$0" in
*mrep*|*mbnc*|*mfwd*) mseq -f | mseq -S ;;
esac
exit 0
;;
@ -504,7 +465,7 @@ while :; do
;;
e|edit)
c=
if ! ${VISUAL:-${EDITOR:-vi}} "$draft"; then
if ! ${EDITOR:-vi} "$draft"; then
c=d
else
if checksensible "$draft"; then
@ -549,7 +510,7 @@ while :; do
mshow -t "$draftmime"
c=
;;
p|preview|show)
show)
if [ -e "$draftmime" ]; then
mshow "$draftmime"
else
@ -558,7 +519,7 @@ while :; do
c=
;;
*)
printf 'What now? (%s[s]end, [c]ancel, [d]elete, [e]dit, [m]ime, [p]review, sign, encrypt) ' "${automime:+mime and }"
printf 'What now? (%s[s]end, [c]ancel, [d]elete, [e]dit, [m]ime, sign, encrypt) ' "${automime:+mime and }"
read -r c
;;
esac

@ -1,17 +1,11 @@
#include <time.h>
#include <unistd.h>
#include "xpledge.h"
int
main()
{
char buf[64];
time_t now;
xpledge("stdio", "");
now = time(0);
time_t now = time(0);
ssize_t l = strftime(buf, sizeof buf,
"%a, %d %b %Y %T %z\n", localtime(&now));

@ -10,11 +10,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
/*
design rationale:
@ -107,7 +105,7 @@ try_again:
snprintf(tmp, sizeof tmp, "%s/tmp/%s", targetdir, id);
if (try_rename) {
snprintf(dst, sizeof dst, "%s/%s/%s"MAILDIR_COLON_SPEC_VER_COMMA"%s",
snprintf(dst, sizeof dst, "%s/%s/%s:2,%s",
targetdir, cflag ? "cur" : "new", id, Xflag);
if (rename(infilename, dst) == 0)
goto success;
@ -147,8 +145,6 @@ try_again:
int in_header = 1;
int is_old = 0;
int prev_line_empty = 0;
int this_line_empty = 0; // only for mbox parsing
while (1) {
errno = 0;
ssize_t rd = getdelim(&line, &linelen, '\n', infile);
@ -160,12 +156,8 @@ try_again:
char *line_start = line;
if (line[0] == '\n' && (!line[1] ||
(line[1] == '\r' && !line[2]))) {
this_line_empty = Mflag ? 1 : 0;
(line[1] == '\r' && !line[2])))
in_header = 0;
} else {
this_line_empty = 0;
}
if (Mflag && strncmp("From ", line, 5) == 0)
break;
@ -195,15 +187,8 @@ try_again:
}
}
// print delayed empty line
if (prev_line_empty)
if (fputc('\n', outfile) == EOF)
goto fail;
if (!this_line_empty)
if (fwrite(line_start, 1, rd, outfile) != (size_t)rd)
goto fail;
prev_line_empty = this_line_empty;
if (fwrite(line_start, 1, rd, outfile) != (size_t)rd)
goto fail;
}
if (fflush(outfile) == EOF)
goto fail;
@ -257,7 +242,7 @@ try_again:
#endif
}
snprintf(dst, sizeof dst, "%s/%s/%s"MAILDIR_COLON_SPEC_VER_COMMA"%s",
snprintf(dst, sizeof dst, "%s/%s/%s:2,%s",
targetdir, (cflag || is_old) ? "cur" : "new", id,
Xflag ? Xflag : statusflags);
if (rename(tmp, dst) != 0)
@ -285,7 +270,7 @@ refile(char *file)
file++;
// keep flags
char *flags = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
char *flags = strstr(file, ":2,");
if (flags)
Xflag = flags + 3;
else
@ -355,12 +340,6 @@ usage2:
if (argc != optind+1)
goto usage2;
xpledge("stdio rpath wpath cpath fattr", "");
if (!preserve_mtime && !Mflag) {
/* drop fattr */
xpledge("stdio rpath wpath cpath", "");
}
targetdir = argv[optind];
gethost();

@ -3,18 +3,13 @@
#include <dirent.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "blaze822.h"
#include "blaze822_priv.h"
#include "xpledge.h"
static char sep = '\n';
int aflag;
void
pwd()
@ -51,9 +46,10 @@ mdirs(char *fpath)
}
while ((d = readdir(dir))) {
if (!DIR_DT(d->d_type))
#if defined(DT_DIR) && defined(DT_UNKNOWN)
if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN)
continue;
#endif
if (d->d_name[0] == '.' &&
d->d_name[1] == 0)
continue;
@ -62,7 +58,7 @@ mdirs(char *fpath)
d->d_name[2] == 0)
continue;
if (!aflag && dotonly && d->d_name[0] != '.')
if (dotonly && d->d_name[0] != '.')
continue;
mdirs(d->d_name);
@ -74,57 +70,21 @@ mdirs(char *fpath)
closedir(dir);
}
char *
profile_maildir()
{
char *f = blaze822_home_file("profile");
struct message *config = blaze822(f);
char *maildir;
static char path[PATH_MAX];
if (!config)
return 0;
if (!(maildir = blaze822_hdr(config, "maildir")))
return 0;
if (strncmp(maildir, "~/", 2) == 0) {
const char *home = getenv("HOME");
if (!home) {
struct passwd *pw = getpwuid(getuid());
home = pw ? pw->pw_dir : "/dev/null/homeless";
}
snprintf(path, sizeof path, "%s/%s", home, maildir+2);
maildir = path;
}
return maildir;
}
int
main(int argc, char *argv[])
{
int c, i;
while ((c = getopt(argc, argv, "0a")) != -1)
while ((c = getopt(argc, argv, "0")) != -1)
switch (c) {
case '0': sep = '\0'; break;
case 'a': aflag = 1; break;
default:
usage:
fprintf(stderr, "Usage: mdirs [-0a] dirs...\n");
fprintf(stderr, "Usage: mdirs [-0] dirs...\n");
exit(1);
}
xpledge("stdio rpath", "");
if (argc == optind) {
char *maildir = profile_maildir();
if (maildir) {
mdirs(maildir);
return 0;
}
if (argc == optind)
goto usage;
}
char toplevel[PATH_MAX];
if (!getcwd(toplevel, sizeof toplevel)) {

@ -10,7 +10,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static int Sflag;
@ -33,7 +32,7 @@ export(char *file)
}
char from[1024] = "nobody";
time_t date = 0;
time_t date = -1;
if (fseek(infile, 0L, SEEK_SET) && errno == ESPIPE) {
date = time(0);
@ -68,7 +67,7 @@ export(char *file)
char *line = 0;
size_t linelen = 0;
printf("From %s %s", from, asctime(gmtime(&date)));
printf("From %s %s", from, ctime(&date));
int in_header = 1;
int final_nl = 0;
@ -87,7 +86,7 @@ export(char *file)
if (in_header && line[0] == '\n' && !line[1]) {
if (Sflag) {
char *flags = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
char *flags = strstr(file, ":2,");
if (!flags)
flags = "";
@ -125,9 +124,6 @@ export(char *file)
if (!final_nl)
putchar('\n');
// ensure empty line at end of message
putchar('\n');
fclose(infile);
}
@ -145,8 +141,6 @@ main(int argc, char *argv[])
status = 0;
xpledge("stdio rpath", "");
if (argc == optind && isatty(0))
blaze822_loop1(":", export);
else

@ -12,8 +12,8 @@
#include <unistd.h>
#include "blaze822.h"
#include "blaze822_priv.h"
#include "xpledge.h"
#define uc(c) ((c) & 0xdf)
static int8_t flags[255];
static int vflag = 0;
@ -47,7 +47,7 @@ flag(char *file)
while (file[indent] == ' ' || file[indent] == '\t')
indent++;
char *f = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
char *f = strstr(file, ":2,");
if (!f)
goto skip;
@ -135,8 +135,6 @@ main(int argc, char *argv[])
exit(1);
}
xpledge("stdio rpath cpath", "");
curfile = blaze822_seq_cur();
if (vflag) {
@ -145,7 +143,7 @@ main(int argc, char *argv[])
return 0;
}
args = calloc(argsalloc, sizeof (char *));
args = calloc(sizeof (char *), argsalloc);
if (!args)
exit(-1);

@ -1,7 +1,3 @@
#ifdef __sun
#define __EXTENSIONS__ /* to get TIOCGWINSZ */
#endif
#include <sys/ioctl.h>
#include <sys/stat.h>
@ -11,12 +7,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <termios.h>
#include "blaze822.h"
#include "xpledge.h"
int column = 0;
int maxcolumn = 80;
@ -34,6 +27,28 @@ chgquote(int quotes)
}
}
void
fixed(int quotes, char *line, size_t linelen)
{
chgquote(quotes);
if (column && linelen > (size_t)(maxcolumn - column)) {
putchar('\n');
column = 0;
}
if (column == 0) {
for (; column < quotes; column++)
putchar('>');
if (quotes && *line != ' ')
putchar(' ');
}
fwrite(line, 1, linelen, stdout);
putchar('\n');
column = 0;
}
void
flowed(int quotes, char *line, ssize_t linelen)
{
@ -79,15 +94,6 @@ flowed(int quotes, char *line, ssize_t linelen)
}
}
void
fixed(int quotes, char *line, size_t linelen)
{
flowed(quotes, line, linelen);
putchar('\n');
column = 0;
}
int
main(int argc, char *argv[])
{
@ -101,8 +107,6 @@ main(int argc, char *argv[])
int force = 0;
int delsp = 0;
xpledge("stdio rpath tty", "");
char *ct = getenv("PIPE_CONTENTTYPE");
if (ct) {
char *s, *se;
@ -126,8 +130,6 @@ main(int argc, char *argv[])
}
}
xpledge("stdio", "");
char *maxcols = getenv("MAXCOLUMNS");
if (maxcols && isdigit(*maxcols)) {
int m = atoi(maxcols);
@ -192,11 +194,6 @@ main(int argc, char *argv[])
if (delsp)
line[--rd] = 0;
flowed(quotes, line, rd);
} else if (rd == 0) { // empty line is fixed
if (column > 0)
putchar('\n');
putchar('\n');
column = 0;
} else {
if (force && rd > maxcolumn) {
flowed(quotes, line, rd);

@ -13,7 +13,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
void
printb36(uint64_t x)
@ -37,8 +36,6 @@ int main()
char *f = blaze822_home_file("profile");
struct message *config = blaze822(f);
xpledge("stdio rpath dns", "");
if (config) // try FQDN: first
host = blaze822_hdr(config, "fqdn");

@ -7,11 +7,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static char *hflag;
static char *pflag;
@ -87,19 +85,19 @@ void
print_addresses(char *s)
{
char *disp, *addr;
char ddec[4096];
char sdec[4096];
if (dflag) {
blaze822_decode_rfc2047(sdec, s, sizeof sdec, "UTF-8");
sdec[sizeof sdec - 1] = 0;
s = sdec;
}
while ((s = blaze822_addr(s, &disp, &addr))) {
if (Hflag && addr)
printf("%s\t", curfile);
if (disp && addr) {
if (dflag) {
blaze822_decode_rfc2047(ddec, disp, sizeof ddec, "UTF-8");
ddec[sizeof ddec - 1] = 0;
disp = ddec;
}
print_quoted(disp);
printf(" <%s>\n", addr);
} else if (addr) {
@ -247,8 +245,6 @@ main(int argc, char *argv[])
status = 1;
xpledge("stdio rpath", "");
if (argc == optind && isatty(0))
blaze822_loop1(".", header);
else

@ -11,8 +11,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "blaze822_priv.h"
#include "xpledge.h"
static int qflag;
static int status;
@ -37,17 +35,17 @@ inc(char *dir)
}
while ((d = readdir(fd))) {
if (!MAIL_DT(d->d_type))
#if defined(DT_REG) && defined(DT_UNKNOWN)
if (d->d_type != DT_REG && d->d_type != DT_UNKNOWN)
continue;
#endif
if (d->d_name[0] == '.')
continue;
snprintf(src, sizeof src, "%s/new/%s",
dir, d->d_name);
snprintf(dst, sizeof dst, "%s/cur/%s%s",
dir, d->d_name,
strstr(d->d_name, MAILDIR_COLON_SPEC_VER_COMMA) ? "" : MAILDIR_COLON_SPEC_VER_COMMA);
dir, d->d_name, strstr(d->d_name, ":2,") ? "" : ":2,");
if (rename(src, dst) < 0) {
fprintf(stderr, "minc: can't rename '%s' to '%s': %s\n",
src, dst, strerror(errno));
@ -75,17 +73,12 @@ usage:
exit(1);
}
xpledge("stdio rpath cpath", "");
if (optind == argc)
goto usage;
status = 0;
if (optind == argc) {
if (isatty(0))
goto usage;
blaze822_loop(0, 0, inc);
} else {
for (i = optind; i < argc; i++)
inc(argv[i]);
}
for (i = optind; i < argc; i++)
inc(argv[i]);
return status;
}

19
mless

@ -22,16 +22,7 @@ if [ "$1" = --filter ]; then
fi
mseq -C "$2"
total=$(mscan -n -- -1)
case $2 in
1) mscan .-0:.+5 ;;
2) mscan .-1:.+4 ;;
$((total - 2))) mscan .-3:.+2 ;;
$((total - 1))) mscan .-4:.+1 ;;
$total) mscan .-5:.+0 ;;
*) mscan .-2:.+3 ;;
esac 2>/dev/null | colorscan
mscan .-2:.+3 2>/dev/null | colorscan
echo
if ! [ -f "$(mseq -r "$2")" ]; then
@ -72,13 +63,7 @@ nl="
"
export MLESS_RAW=0
export MLESS_HTML=0
if [ -f "$MBLAZE/mlesskey" ]; then
export LESSKEYIN="$MBLAZE/mlesskey"
elif [ -f "$HOME/.mblaze/mlesskey" ]; then
export LESSKEYIN="$HOME/.mblaze/mlesskey"
elif [ -f "$HOME/.mlesskey" ]; then
export LESSKEYIN="$HOME/.mlesskey"
elif [ -f "$MBLAZE/mless" ]; then
if [ -f "$MBLAZE/mless" ]; then
export LESSKEY="$MBLAZE/mless"
elif [ -f "$HOME/.mblaze/mless" ]; then
export LESSKEY="$HOME/.mblaze/mless"

@ -1,7 +1,5 @@
# mless(1) keybindings
# When using less <590:
# compile with lesskey -o .mless mlesskey and install as ~/.mless
# When using less >=590: copy this file to ~/.mlesskey
# to update: lesskey -o ~/.mless ~/.mlesskey
Q quit \1
:cq quit \1
[ prev-file

@ -12,8 +12,9 @@
#include <unistd.h>
#include "blaze822.h"
#include "blaze822_priv.h"
#include "xpledge.h"
#define lc(c) ((c) | 0x20)
#define uc(c) ((c) & 0xdf)
/*
@ -48,7 +49,6 @@ static long tdirs;
static long tunseen;
static long tflagged;
static long tcount;
static long tmatched;
void
list(char *prefix, char *file)
@ -58,14 +58,14 @@ list(char *prefix, char *file)
if (flagset || iflag) {
size_t prefixlen;
f = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
f = strstr(file, ":2,");
if (!f &&
prefix &&
(prefixlen = strlen(prefix)) &&
prefixlen >= 4 &&
strcmp(prefix + prefixlen - 4, "/new") == 0)
f = MAILDIR_COLON_SPEC_VER_COMMA;
f = ":2,";
}
if (flagset) {
@ -74,12 +74,14 @@ list(char *prefix, char *file)
return;
icount++;
tcount++;
char *g;
for (g = f + 3; *g; g++)
if (flags[(unsigned int)*g] == -1)
f += 3;
while (*f) {
if (flags[(unsigned int)*f] == -1)
return;
else if (flags[(unsigned int)*g] == 1)
if (flags[(unsigned int)*f] == 1)
sum++;
f++;
}
if (sum != flagsum)
return;
}
@ -88,7 +90,6 @@ list(char *prefix, char *file)
if (!f)
return;
imatched++;
tmatched++;
if (!flagset)
icount++, tcount++;
if (!strchr(f, 'S'))
@ -111,8 +112,8 @@ list(char *prefix, char *file)
#include <sys/syscall.h>
struct linux_dirent64 {
uint64_t d_ino; /* 64-bit inode number */
int64_t d_off; /* 64-bit offset to next structure */
ino64_t d_ino; /* 64-bit inode number */
off64_t d_off; /* 64-bit offset to next structure */
unsigned short d_reclen; /* Size of this dirent */
unsigned char d_type; /* File type */
char d_name[]; /* Filename (null-terminated) */
@ -146,7 +147,7 @@ listdir(char *dir)
for (bpos = 0; bpos < nread; bpos += d->d_reclen) {
d = (struct linux_dirent64 *)(buf + bpos);
if (!MAIL_DT(d->d_type))
if (d->d_type != DT_REG && d->d_type != DT_UNKNOWN)
continue;
if (d->d_name[0] == '.')
continue;
@ -167,7 +168,7 @@ listdir(char *dir)
if (!fd)
return;
while ((d = readdir(fd))) {
if (!MAIL_DT(d->d_type))
if (d->d_type != DT_REG && d->d_type != DT_UNKNOWN)
continue;
if (d->d_name[0] == '.')
continue;
@ -218,12 +219,8 @@ listarg(char *arg)
if (iflag && (imatched || (maildir && !flagset))) {
tdirs++;
if (flagset && imatched)
printf("%6ld matched %6ld unseen %3ld flagged %6ld msg %s\n",
imatched, iunseen, iflagged, icount, arg);
else
printf("%6ld unseen %3ld flagged %6ld msg %s\n",
iunseen, iflagged, icount, arg);
printf("%6ld unseen %3ld flagged %6ld msg %s\n",
iunseen, iflagged, icount, arg);
}
icount = gcount;
@ -273,8 +270,6 @@ usage:
int i;
xpledge("stdio rpath", "");
for (i = 0, flagsum = 0, flagset = 0; (size_t)i < sizeof flags; i++) {
if (flags[i] != 0)
flagset++;
@ -291,14 +286,9 @@ usage:
listarg(argv[i]);
}
if (iflag && tdirs > 1) {
if (flagset)
printf("%6ld matched %6ld unseen %3ld flagged %6ld msg\n",
tmatched, tunseen, tflagged, tcount);
else
printf("%6ld unseen %3ld flagged %6ld msg\n",
tunseen, tflagged, tcount);
}
if (iflag && tdirs > 1)
printf("%6ld unseen %3ld flagged %6ld msg\n",
tunseen, tflagged, tcount);
return 0;
}

@ -1,7 +1,6 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@ -12,12 +11,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static int cflag;
static int rflag;
@ -60,11 +57,6 @@ int gen_b64(uint8_t *s, off_t size)
return 0;
}
#define qphrasevalid(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || \
(c >= 'a' && c <= 'z') || \
c == '!' || c == '*' || c == '+' || c == '-' || \
c == '/')
size_t
gen_qp(uint8_t *s, off_t size, size_t maxlinelen, size_t linelen)
{
@ -129,10 +121,6 @@ gen_qp(uint8_t *s, off_t size, size_t maxlinelen, size_t linelen)
putc_unlocked('\n', stdout);
linelen = 0;
prev = 0;
} else if (header && !qphrasevalid(s[i])) {
printf("=%02X", s[i]);
linelen += 3;
prev = '_';
} else {
putc_unlocked(s[i], stdout);
linelen++;
@ -313,41 +301,23 @@ print_header(char *line) {
}
int prevq = 0; // was the previous word encoded as qp?
char prevqs = 0; // was the previous word a quoted-string?
ssize_t linelen = s - line;
while (*s) {
size_t highbit = 0;
int qs = 0;
e = s;
while (*e && *e == ' ')
e++;
if (*e == '"') { // scan quoted-string, encode at once
s = e;
for (e++; *e && *e != '"'; e++) {
if (*e == '\\')
e++;
if ((uint8_t)*e >= 127)
highbit++;
}
if (*e == '"')
e++;
qs = 1;
} else { // scan word
while (*e && *e == ' ')
e++;
for (; *e && *e != ' '; e++) {
if ((uint8_t)*e >= 127)
highbit++;
}
for (; *e && *e != ' '; e++) {
if ((uint8_t)*e >= 127)
highbit++;
}
if (!highbit) {
if (e-s >= 998)
goto force_qp;
if (e-s >= 78 - linelen && linelen > 0) {
if (e-s >= 78 - linelen) {
// wrap in advance before long word
printf("\n");
linelen = 0;
@ -356,7 +326,7 @@ print_header(char *line) {
// space at beginning of line
goto force_qp;
}
if (*s != ' ' && !(prevqs && !prevq && *(s-1) != ' ')) {
if (*s != ' ') {
printf(" ");
linelen++;
}
@ -367,12 +337,6 @@ print_header(char *line) {
force_qp:
if (!prevq && *s == ' ')
s++;
if (qs && *s == '"')
s++;
if (qs && e > s && *(e-1) == '"')
e--;
if (linelen >= 78 - 13 - 4 ||
(e-s < (78 - 13)/3 &&
e-s >= (78 - linelen - 13)/3)) {
@ -386,33 +350,12 @@ force_qp:
printf("?=");
linelen += 2;
prevq = 1;
if (qs && *e == '"')
e++;
}
prevqs = qs;
s = e;
}
printf("\n");
}
static int
valid_content_type(char *s)
{
int slash = 0;
for (; *s; s++)
if (*s == '/')
slash++;
else if (isalnum(*s) || *s == '-' || *s == '+' || *s == '.' ||
*s == ';' || *s == '=' || *s == '#')
; /* ok */
else
return 0;
return slash == 1;
}
int
gen_build()
{
@ -424,45 +367,30 @@ gen_build()
size_t linelen = 0;
int inheader = 1;
int intext = 0;
int emptybody = 1;
int ret = 0;
char *contenttype = 0;
char *contenttransferenc = 0;
while (1) {
ssize_t read = getdelim(&line, &linelen, '\n', stdin);
if (read == -1) {
if (feof(stdin)) {
if (!emptybody)
break;
line = strdup(inheader ? "\n" : "");
} else { // errored
if (feof(stdin))
break;
else
exit(1);
}
}
if (inheader) {
if (line[0] == '\n') {
inheader = 0;
printf("MIME-Version: 1.0\n");
if (rflag) {
printf("Content-Type:%s", contenttype ? contenttype : " text/plain; charset=UTF-8\n");
printf("Content-Transfer-Encoding:%s", contenttransferenc ? contenttransferenc : " quoted-printable\n");
printf("\n");
printf("Content-Type: text/plain; charset=UTF-8\n");
printf("Content-Transfer-Encoding: quoted-printable\n\n");
} else {
printf("Content-Type: %s; boundary=\"%s\"\n", tflag, sep);
printf("\n");
printf("This is a multipart message in MIME format.\n");
}
} else {
if (strncasecmp(line, "Content-Type:", 13) == 0) {
free(contenttype);
contenttype = strdup(line+13);
} else if (strncasecmp(line, "Content-Transfer-Encoding:", 26) == 0) {
free(contenttransferenc);
contenttransferenc = strdup(line+26);
} else {
print_header(line);
}
print_header(line);
}
continue;
}
@ -472,14 +400,12 @@ gen_build()
if (f) {
char of = *f;
*f = 0;
if (valid_content_type(line+1)) {
if (strchr(line, '/')) {
printf("\n--%s\n", sep);
if (line[read-1] == '\n')
line[read-1] = 0;
if (gen_file(f+1, line+1) != 0)
ret = 1;
gen_file(f+1, (char *)line+1);
intext = 0;
emptybody = 0;
continue;
}
*f = of;
@ -488,26 +414,20 @@ gen_build()
if (!rflag && !intext) {
printf("\n--%s\n", sep);
printf("Content-Type:%s", contenttype ? contenttype : " text/plain; charset=UTF-8\n");
printf("Content-Type: text/plain; charset=UTF-8\n");
printf("Content-Disposition: inline\n");
printf("Content-Transfer-Encoding:%s", contenttransferenc ? contenttransferenc : " quoted-printable\n");
printf("\n");
printf("Content-Transfer-Encoding: quoted-printable\n\n");
intext = 1;
}
if (contenttransferenc)
printf("%s", line);
else
gen_qp((uint8_t *)line, strlen(line), 78, 0);
emptybody = 0;
gen_qp((uint8_t *)line, strlen(line), 78, 0);
}
if (!rflag && !inheader)
printf("\n--%s--\n", sep);
free(line);
return ret;
return 0;
}
int
@ -518,7 +438,6 @@ check()
off_t linelen = 0;
off_t maxheadlinelen = 0;
off_t maxbodylinelen = 0;
off_t bodylinelenlimit = getenv("MBLAZE_RELAXED_MIME") ? 998 : 78;
int c;
int l = -1;
@ -556,7 +475,7 @@ check()
}
if (bitlow == 0 && bithigh == 0 &&
maxheadlinelen < 998 && maxbodylinelen <= bodylinelenlimit &&
maxheadlinelen < 998 && maxbodylinelen <= 78 &&
l == '\n')
return 0;
else
@ -584,8 +503,6 @@ usage:
if (argc != optind)
goto usage;
xpledge("stdio rpath", "");
if (cflag)
return check();

@ -5,7 +5,7 @@ umask 077
r=0
for dir; do
mkdir -p "$dir"/tmp "$dir"/new "$dir"/cur || r=1
mkdir -p "$dir"/cur "$dir"/new "$dir"/tmp || r=1
done
exit $r

File diff suppressed because it is too large Load Diff

@ -1,11 +1,6 @@
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 700
#endif
#ifdef __sun
#define __EXTENSIONS__ /* to get TIOCGWINSZ */
#endif
#include "xpledge.h"
#include <sys/ioctl.h>
#include <sys/stat.h>
@ -22,7 +17,6 @@
#include <time.h>
#include <unistd.h>
#include <wchar.h>
#include <termios.h>
#include "blaze822.h"
#include "u8decode.h"
@ -322,7 +316,7 @@ oneline(char *file)
}
struct message *msg = blaze822(file);
char *flags = msg ? strstr(file, MAILDIR_COLON_SPEC_VER_COMMA) : 0;
char *flags = msg ? strstr(file, ":2,") : 0;
if (!flags)
flags = "";
else
@ -555,8 +549,6 @@ main(int argc, char *argv[])
exit(1);
}
xpledge("stdio rpath tty proc exec", NULL);
if (nflag) {
if (argc == optind && isatty(0))
blaze822_loop1(":", numline);
@ -575,13 +567,9 @@ main(int argc, char *argv[])
struct winsize w;
int ttyfd = open("/dev/tty", O_RDONLY | O_NOCTTY);
if (ttyfd >= 0) {
if (ioctl(ttyfd, TIOCGWINSZ, &w) == 0)
cols = w.ws_col;
close(ttyfd);
}
if (ttyfd >= 0 && ioctl(ttyfd, TIOCGWINSZ, &w) == 0) {
cols = w.ws_col;
if (isatty(1)) {
char *pg;
pg = getenv("MBLAZE_PAGER");
if (!pg)
@ -594,9 +582,8 @@ main(int argc, char *argv[])
pg, strerror(errno));
}
}
xpledge("stdio rpath", "");
if (ttyfd >= 0)
close(ttyfd);
if (getenv("COLUMNS"))
cols = atoi(getenv("COLUMNS"));
if (cols <= 40)

@ -11,7 +11,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static char *expr;
@ -138,7 +137,7 @@ sed(char *file)
if (*s == ':')
*s = '|';
int rv;
if ((rv = regcomp(&headerrx, headersel, REG_EXTENDED | REG_ICASE)) != 0) {
if ((rv = regcomp(&headerrx, headersel, REG_EXTENDED)) != 0) {
char buf[100];
regerror(rv, &headerrx, buf, sizeof buf);
fprintf(stderr, "msed: %s\n", buf);
@ -324,8 +323,6 @@ main(int argc, char *argv[])
exit(1);
}
xpledge("stdio rpath", "");
expr = argv[optind];
optind++;

@ -12,8 +12,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "blaze822_priv.h"
#include "xpledge.h"
static int fflag;
static int rflag;
@ -60,9 +58,10 @@ namescan(char *dir)
if (!fd)
return;
while ((d = readdir(fd))) {
if (!MAIL_DT(d->d_type))
#if defined(DT_REG) && defined(DT_UNKNOWN)
if (d->d_type != DT_REG && d->d_type != DT_UNKNOWN)
continue;
#endif
if (d->d_name[0] == '.')
continue;
@ -70,7 +69,7 @@ namescan(char *dir)
snprintf(file, sizeof file, "%s/%s", dir, d->d_name);
char *e;
if ((e = strstr(d->d_name, MAILDIR_COLON_SPEC_VER_COMMA)))
if ((e = strstr(d->d_name, ":2,")))
*e = 0;
struct name *c = malloc(sizeof (struct name));
@ -101,7 +100,7 @@ search(char *file)
if (!namefind(dir))
namescan(dir);
if ((e = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA)))
if ((e = strstr(file, ":2,")))
*e = 0;
return namefind(file);
@ -131,11 +130,11 @@ fix(FILE *out, char *file)
char *e;
char *sep;
if ((e = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA))) {
if ((e = strstr(file, ":2,"))) {
sep = "";
e[3] = 0;
} else {
sep = MAILDIR_COLON_SPEC_VER_COMMA;
sep = ":2,";
}
snprintf(buf, sizeof buf, "%s%s", file, sep);
if (access(buf, F_OK) == 0) goto ok;
@ -299,8 +298,6 @@ usage:
exit(1);
}
xpledge("stdio rpath wpath cpath", "");
if (cflag)
blaze822_loop1(cflag, overridecur);
@ -317,10 +314,14 @@ usage:
if (optind == argc && !isatty(0))
return stdinmode();
char *seq = blaze822_seq_open(0);
if (!seq)
return 1;
int i;
char *f;
char *a;
char *seq = 0;
struct blaze822_seq_iter iter = { 0 };
if (optind == argc) {
a = ":";
@ -334,14 +335,6 @@ hack:
printf("%s\n", a);
continue;
}
if (!seq) {
seq = blaze822_seq_open(0);
if (!seq)
return 1;
}
struct blaze822_seq_iter iter = { 0 };
while ((f = blaze822_seq_next(seq, a, &iter))) {
char *s = f;
if (rflag)

@ -14,7 +14,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static int Bflag;
static int rflag;
@ -51,10 +50,10 @@ printable(int c)
}
size_t
print_ascii(char *body, size_t bodylen, int oneline)
print_ascii(char *body, size_t bodylen)
{
if (safe_output) {
safe_u8putstr(body, bodylen, oneline, stdout);
safe_u8putstr(body, bodylen, stdout);
return bodylen;
} else {
return fwrite(body, 1, bodylen, stdout);
@ -73,7 +72,7 @@ printhdr(char *hdr)
}
if (*hdr) {
print_ascii(hdr, strlen(hdr), 1);
print_ascii(hdr, strlen(hdr));
fputc('\n', stdout);
}
}
@ -85,7 +84,7 @@ print_u8recode(char *body, size_t bodylen, char *srcenc)
ic = iconv_open("UTF-8", srcenc);
if (ic == (iconv_t)-1) {
fprintf(stderr, "unsupported encoding: %s\n", srcenc);
printf("unsupported encoding: %s\n", srcenc);
return;
}
@ -98,7 +97,7 @@ print_u8recode(char *body, size_t bodylen, char *srcenc)
size_t r = iconv(ic, &body, &bodylen, &bufptr, &buflen);
if (bufptr != buf) {
print_ascii(buf, bufptr-buf, 0);
print_ascii(buf, bufptr-buf);
final_char = bufptr[-1];
}
@ -107,7 +106,7 @@ print_u8recode(char *body, size_t bodylen, char *srcenc)
buflen = sizeof buf;
r = iconv(ic, 0, 0, &bufptr, &buflen);
if (bufptr != buf) {
print_ascii(buf, bufptr-buf, 0);
print_ascii(buf, bufptr-buf);
final_char = bufptr[-1];
}
if (r != (size_t)-1)
@ -185,23 +184,11 @@ print_filename(char *filename)
{
if (filename) {
printf(" name=\"");
safe_u8putstr(filename, strlen(filename), 1, stdout);
safe_u8putstr(filename, strlen(filename), stdout);
printf("\"");
}
}
static int
probably_utf8(char *charset)
{
return !charset ||
strcasecmp(charset, "utf-8") == 0 ||
strcasecmp(charset, "utf8") == 0 ||
strcasecmp(charset, "us-ascii") == 0 ||
/* best guess for: */
strcasecmp(charset, "unknown-8bit") == 0 ||
strcasecmp(charset, "x-unknown") == 0;
}
blaze822_mime_action
render_mime(int depth, struct message *msg, char *body, size_t bodylen)
{
@ -252,7 +239,7 @@ render_mime(int depth, struct message *msg, char *body, size_t bodylen)
printf(" render=\"%s\" ---\n", cmd);
if (outlen) {
if (e == 0)
print_ascii(output, outlen, 0);
print_ascii(output, outlen);
else
fwrite(output, 1, outlen, stdout);
if (output[outlen-1] != '\n')
@ -298,8 +285,11 @@ nofilter:
char *charset = 0, *cs, *cse;
if (blaze822_mime_parameter(ct, "charset", &cs, &cse))
charset = strndup(cs, cse-cs);
if (probably_utf8(charset)) {
print_ascii(body, bodylen, 0);
if (!charset ||
strcasecmp(charset, "utf-8") == 0 ||
strcasecmp(charset, "utf8") == 0 ||
strcasecmp(charset, "us-ascii") == 0) {
print_ascii(body, bodylen);
if (bodylen > 0 && body[bodylen-1] != '\n')
putchar('\n');
} else {
@ -316,10 +306,9 @@ nofilter:
}
printf("\n");
} else if (strncmp(ct, "multipart/alternative", 21) == 0) {
if (strcmp(Aflag, "all") != 0) {
choose_alternative(msg, depth);
r = MIME_PRUNE;
} // else default blaze822_mime_walk action
choose_alternative(msg, depth);
r = MIME_PRUNE;
} else if (strncmp(ct, "multipart/", 10) == 0) {
; // default blaze822_mime_walk action
} else {
@ -336,7 +325,7 @@ nofilter:
static void
choose_alternative(struct message *msg, int depth)
{
int n = 0;
int n = 1;
int m = 0;
char *p = Aflag + strlen(Aflag);
@ -363,13 +352,6 @@ choose_alternative(struct message *msg, int depth)
}
blaze822_free(imsg);
if (n == 0) {
// No part matched, use last part as per RFC1341 7.2.3
// "In general, choosing the best type means displaying
// only the LAST part that can be displayed."
n = m;
}
imsg = 0;
while (blaze822_multipart(msg, &imsg))
if (--n == 0)
@ -390,8 +372,11 @@ reply_mime(int depth, struct message *msg, char *body, size_t bodylen)
char *charset = 0, *cs, *cse;
if (blaze822_mime_parameter(ct, "charset", &cs, &cse))
charset = strndup(cs, cse-cs);
if (probably_utf8(charset))
print_ascii(body, bodylen, 0);
if (!charset ||
strcasecmp(charset, "utf-8") == 0 ||
strcasecmp(charset, "utf8") == 0 ||
strcasecmp(charset, "us-ascii") == 0)
print_ascii(body, bodylen);
else
print_u8recode(body, bodylen, charset);
reply_found++;
@ -502,7 +487,7 @@ extract_mime(int depth, struct message *msg, char *body, size_t bodylen)
fwrite(body, 1, bodylen, stdout);
} else { // extract all named attachments
if (filename) {
safe_u8putstr(filename, strlen(filename), 1, stdout);
safe_u8putstr(filename, strlen(filename), stdout);
printf("\n");
writefile(filename, body, bodylen);
}
@ -563,7 +548,7 @@ extract_mime(int depth, struct message *msg, char *body, size_t bodylen)
fwrite(body, 1, bodylen, stdout);
}
} else {
safe_u8putstr(filename, strlen(filename), 1, stdout);
safe_u8putstr(filename, strlen(filename), stdout);
printf("\n");
writefile(filename, body, bodylen);
}
@ -609,7 +594,7 @@ print_date_header(char *v)
}
printf("Date: ");
print_ascii(v, strlen(v), 1);
print_ascii(v, strlen(v));
time_t t = blaze822_date(v);
if (t == -1) {
@ -695,7 +680,7 @@ print_decode_header(char *h, char *v)
printhdr(h);
fputc(':', stdout);
fputc(' ', stdout);
print_ascii(d, strlen(d), 1);
print_ascii(d, strlen(d));
fputc('\n', stdout);
}
@ -762,7 +747,7 @@ show(char *file)
printf("\n");
if (rflag) { // raw body
print_ascii(blaze822_body(msg), blaze822_bodylen(msg), 0);
print_ascii(blaze822_body(msg), blaze822_bodylen(msg));
goto done;
}
@ -806,9 +791,7 @@ main(int argc, char *argv[])
exit(1);
}
xpledge("stdio rpath wpath cpath proc exec", NULL);
if (!rflag && !xflag && !Oflag && !Rflag && !tflag)
if (!rflag && !Oflag && !Rflag)
safe_output = 1;
if (safe_output && isatty(1)) {
@ -833,22 +816,17 @@ main(int argc, char *argv[])
}
if (xflag) { // extract
xpledge("stdio rpath wpath cpath", NULL);
extract(xflag, argc-optind, argv+optind, 0);
} else if (Oflag) { // extract to stdout
xpledge("stdio rpath", NULL);
extract(Oflag, argc-optind, argv+optind, 1);
} else if (tflag) { // list
xpledge("stdio rpath", NULL);
if (argc == optind && isatty(0))
blaze822_loop1(".", list);
else
blaze822_loop(argc-optind, argv+optind, list);
} else if (Rflag) { // render for reply
xpledge("stdio rpath", NULL);
blaze822_loop(argc-optind, argv+optind, reply);
} else { // show
/* XXX pledge: still r/w on the whole file-system + fork/exec */
if (!(qflag || rflag || Fflag)) {
char *f = getenv("MAILFILTER");
if (!f)
@ -861,8 +839,7 @@ main(int argc, char *argv[])
else
blaze822_loop(argc-optind, argv+optind, show);
if (!nflag) // don't set cur
if (newcur)
blaze822_seq_setcur(newcur);
blaze822_seq_setcur(newcur);
}
if (pid2 > 0)

@ -10,7 +10,6 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
struct mail {
char *file;
@ -219,8 +218,8 @@ unreadorder(const void *a, const void *b)
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
char *fa = strstr(ia->file, MAILDIR_COLON_SPEC_VER_COMMA);
char *fb = strstr(ib->file, MAILDIR_COLON_SPEC_VER_COMMA);
char *fa = strstr(ia->file, ":2,");
char *fb = strstr(ib->file, ":2,");
int unreada = fa ? !strchr(fa, 'S') : 0;
int unreadb = fb ? !strchr(fb, 'S') : 0;
@ -234,8 +233,8 @@ flaggedorder(const void *a, const void *b)
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
char *fa = strstr(ia->file, MAILDIR_COLON_SPEC_VER_COMMA);
char *fb = strstr(ib->file, MAILDIR_COLON_SPEC_VER_COMMA);
char *fa = strstr(ia->file, ":2,");
char *fb = strstr(ib->file, ":2,");
int unreada = fa ? !!strchr(fa, 'F') : 0;
int unreadb = fb ? !!strchr(fb, 'F') : 0;
@ -279,9 +278,9 @@ add(char *file)
int
order(const void *a, const void *b)
{
int i;
for (i = 0; i < order_idx; i++) {
int r = (sortorders[i])(a, b);
int i, r;
for (i = 0, r = 0; i < order_idx; i++) {
r = (sortorders[i])(a, b);
if (r != 0)
return r;
}
@ -317,9 +316,8 @@ main(int argc, char *argv[])
exit(1);
}
xpledge("stdio rpath", "");
mails = calloc(mailalloc, sizeof (struct mail));
mails = calloc(sizeof (struct mail), mailalloc);
if (!mails)
exit(-1);

@ -19,16 +19,14 @@
#include <unistd.h>
#include "blaze822.h"
#include "xpledge.h"
static int vflag;
static int pflag;
static int rflag;
static int optional;
struct container {
char *mid;
char *file;
struct message *msg;
time_t date;
struct container *parent;
struct container *child;
@ -86,6 +84,7 @@ midcont(char *mid)
exit(111);
c->mid = mid;
c->file = 0;
c->msg = 0;
c->date = -1;
c->optional = 0;
c->parent = c->child = c->next = 0;
@ -102,6 +101,7 @@ store_id(char *file, struct message *msg)
c = midcont(mid(msg));
c->file = strdup(file);
c->msg = msg;
c->optional = optional;
return c;
@ -236,8 +236,6 @@ out2:
c->child = 0;
}
}
blaze822_free(msg);
}
time_t
@ -289,6 +287,7 @@ find_roots()
top = malloc(sizeof (struct container));
if (!top)
exit(111);
top->msg = 0;
top->date = -1;
top->file = 0;
top->next = top->child = top->parent = 0;
@ -313,6 +312,7 @@ prune_tree(struct container *c, int depth)
// turn into child if we don't exist and only have a child
c->mid = c->child->mid;
c->file = c->child->file;
c->msg = c->child->msg;
if (c->child->date > c->date)
c->date = c->child->date;
c->optional = c->child->optional;
@ -347,12 +347,6 @@ dateorder(const void *a, const void *b)
return 0;
}
static int
reverse_dateorder(const void *a, const void *b)
{
return dateorder(b, a);
}
void
sort_tree(struct container *c, int depth)
{
@ -365,15 +359,14 @@ sort_tree(struct container *c, int depth)
if (i == 1) // no sort needed
return;
struct container **a = calloc(i, sizeof (struct container *));
struct container **a = calloc(sizeof (struct container *), i);
if (!a)
return;
for (r = c->child, i = 0; r; r = r->next, i++)
a[i] = r;
qsort(a, i, sizeof (struct container *),
rflag && depth < 0 ? reverse_dateorder : dateorder);
qsort(a, i, sizeof (struct container *), dateorder);
c->child = a[0];
for (j = 0; j+1 < i; j++)
@ -389,8 +382,7 @@ print_tree(struct container *c, int depth)
{
do {
// skip toplevel threads when they are unresolved or all optional
// (or when -p is given, skip those subthreads)
if ((depth <= 1 || pflag) &&
if (depth <= 1 &&
(c->optional || !c->file) &&
(!c->child || alloptional(c->child)))
continue;
@ -414,31 +406,25 @@ int
main(int argc, char *argv[])
{
int c;
long i;
optional = 1;
xpledge("stdio rpath", "");
while ((c = getopt(argc, argv, "S:prv")) != -1)
while ((c = getopt(argc, argv, "S:v")) != -1)
switch (c) {
case 'S': blaze822_loop1(optarg, thread); break;
case 'v': vflag = 1; break;
case 'p': pflag = 1; break;
case 'r': rflag = 1; break;
default:
fprintf(stderr, "Usage: mthread [-vpr] [-S dir] [msgs...]\n");
fprintf(stderr, "Usage: mthread [-v] [-S dir] [msgs...]\n");
exit(1);
}
optional = 0;
if (argc == optind && isatty(0))
blaze822_loop1(":", thread);
i = blaze822_loop1(":", thread);
else
blaze822_loop(argc-optind, argv+optind, thread);
// the tree of all toplevel threads has depth -1,
// so toplevel threads have depth 0.
i = blaze822_loop(argc-optind, argv+optind, thread);
find_roots();
if (!vflag)
@ -446,5 +432,7 @@ main(int argc, char *argv[])
sort_tree(top, -1);
print_tree(top, -1);
fprintf(stderr, "%ld mails threaded\n", i);
return 0;
}

@ -1,28 +1,86 @@
#include <time.h>
time_t
mytimegm(struct tm *tm)
// from musl@1cc81f5cb, slightly tweaked
static long long
__year_to_secs(long long year, int *is_leap)
{
int mon = tm->tm_mon + 1 - 2; /* put March first, Feb last */
long long year = tm->tm_year + 1900;
if (year-2ULL <= 136) {
int y = year;
int leaps = (y-68)>>2;
if (!((y-68)&3)) {
leaps--;
if (is_leap) *is_leap = 1;
} else if (is_leap) *is_leap = 0;
return 31536000*(y-70) + 86400*leaps;
}
int cycles, centuries, leaps, rem;
cycles = (year-100) / 400;
rem = (year-100) % 400;
if (rem < 0) {
cycles--;
rem += 400;
}
if (!rem) {
*is_leap = 1;
centuries = 0;
leaps = 0;
} else {
if (rem >= 200) {
if (rem >= 300) centuries = 3, rem -= 300;
else centuries = 2, rem -= 200;
} else {
if (rem >= 100) centuries = 1, rem -= 100;
else centuries = 0;
}
if (!rem) {
*is_leap = 0;
leaps = 0;
} else {
leaps = rem / 4U;
rem %= 4U;
*is_leap = !rem;
}
}
leaps += 97*cycles + 24*centuries - *is_leap;
if (mon <= 0 || mon >= 12) {
int adj = mon / 12;
mon %= 12;
if (mon <= 0) {
return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
}
static int
__month_to_secs(int month, int is_leap)
{
static const int secs_through_month[] = {
0, 31*86400, 59*86400, 90*86400,
120*86400, 151*86400, 181*86400, 212*86400,
243*86400, 273*86400, 304*86400, 334*86400 };
int t = secs_through_month[month];
if (is_leap && month >= 2) t+=86400;
return t;
}
time_t tm_to_secs(const struct tm *tm)
{
int is_leap;
long long year = tm->tm_year;
int month = tm->tm_mon;
if (month >= 12 || month < 0) {
int adj = month / 12;
month %= 12;
if (month < 0) {
adj--;
mon += 12;
month += 12;
}
year += adj;
}
time_t t = 0;
t += tm->tm_sec;
t += 60 * tm->tm_min;
t += 60*60 * tm->tm_hour;
t += 24*60*60 * (tm->tm_mday - 1);
t += 24*60*60 * (367*mon/12);
t += 24*60*60 * (year/4 - year/100 + year/400);
t += 24*60*60 * (365*year - 719498L);
long long t = __year_to_secs(year, &is_leap);
t += __month_to_secs(month, is_leap);
t += 86400LL * (tm->tm_mday-1);
t += 3600LL * tm->tm_hour;
t += 60LL * tm->tm_min;
t += tm->tm_sec;
return t;
}

@ -86,8 +86,6 @@ blaze822_mime_parameter(char *s, char *name, char **starto, char **stopo)
while (*s) {
while (iswsp(*s))
s++;
if (!*s)
return 0;
if (strncasecmp(s, name, namelen) == 0 && s[namelen] == '=') {
s += namelen + 1;
break;
@ -116,26 +114,6 @@ blaze822_mime_parameter(char *s, char *name, char **starto, char **stopo)
return 1;
}
// like mymemmem but check the match is followed by \r, \n or -.
static char *
mymemmemnl(const char *h0, size_t k, const char *n0, size_t l)
{
char *r;
while (k && (r = mymemmem(h0, k, n0, l))) {
if (r + l < h0 + k && // check if r[l] safe to access
(r[l] == '\r' || r[l] == '\n' || r[l] == '-'))
return r;
else {
// skip over this match
k -= (r - h0) + 1;
h0 = r + 1;
}
}
return 0;
}
int
blaze822_multipart(struct message *msg, struct message **imsg)
{
@ -166,7 +144,7 @@ blaze822_multipart(struct message *msg, struct message **imsg)
else
prevpart = msg->body;
char *part = mymemmemnl(prevpart, msg->bodyend - prevpart, mboundary, boundarylen);
char *part = mymemmem(prevpart, msg->bodyend - prevpart, mboundary, boundarylen);
if (!part)
return 0;
@ -180,10 +158,10 @@ blaze822_multipart(struct message *msg, struct message **imsg)
else
return 0; // XXX error condition?
char *nextpart = mymemmemnl(part, msg->bodyend - part, mboundary, boundarylen);
char *nextpart = mymemmem(part, msg->bodyend - part, mboundary, boundarylen);
if (!nextpart)
nextpart = msg->bodyend; // no boundary found, take all
else if (nextpart == part) // invalid empty MIME part
return 0; // XXX error condition
if (nextpart == part) // invalid empty MIME part
return 0; // XXX error condition
if (*(nextpart-1) == '\n')
@ -204,9 +182,6 @@ blaze822_walk_mime(struct message *msg, int depth, blaze822_mime_callback visit)
blaze822_mime_action r = MIME_CONTINUE;
if (depth > 64)
return MIME_PRUNE;
if (blaze822_mime_body(msg, &ct, &body, &bodylen, &bodychunk)) {
r = visit(depth, msg, body, bodylen);

@ -82,25 +82,16 @@ blaze822_decode_b64(char *s, char *e, char **deco, size_t *decleno)
*deco = buf;
while (s + 4 <= e) {
uint32_t v = 0;
unsigned char t = 0;
#define SKIP_WS \
while (s < e && isfws((unsigned char)*s)) \
s++; \
if (s >= e) \
while (s < e && isfws((unsigned char)*s))
s++;
if (s >= e)
break;
SKIP_WS;
unsigned char c0 = *s++;
SKIP_WS;
unsigned char c1 = *s++;
SKIP_WS;
unsigned char c2 = *s++;
SKIP_WS;
unsigned char c3 = *s++;
uint32_t v = 0;
unsigned char t = 0;
#undef SKIP_WS
unsigned char c0 = s[0], c1 = s[1], c2 = s[2], c3 = s[3];
s += 4;
if ((c0 | c1 | c2 | c3) > 127)
goto error;
@ -201,8 +192,7 @@ blaze822_decode_rfc2047(char *dst, char *src, size_t dlen, char *tgtenc)
if (ic == (iconv_t)-1)
goto nocode;
char enc = lc(*e);
e++;
char enc = lc(*e++);
if (*e++ != '?')
goto nocode;
char *start = e;

@ -4,7 +4,7 @@
#include "u8decode.h"
void
safe_u8putstr(char *s0, size_t l, int oneline, FILE *stream)
safe_u8putstr(char *s0, size_t l, FILE *stream)
{
// tty-safe output of s, with relaxed utf-8 semantics:
// - C0 and C1 are displayed as escape sequences
@ -35,8 +35,7 @@ safe_u8putstr(char *s0, size_t l, int oneline, FILE *stream)
fputc(0x80 | (*s & 0x3f), stream);
}
} else if (c < 32 &&
*s != ' ' && *s != '\t' &&
(oneline || (*s != '\n' && *s != '\r'))) {
*s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') {
// NUL
if (l == 0)
l = 1;

31
seq.c

@ -110,14 +110,15 @@ blaze822_seq_load(char *map)
char *e = t;
while (s < e && isfws(*(e-1)))
e--;
// printf("{%.*s}\n", e-s, s);
char *f = strndup(s, e-s);
if (!f)
return -1;
struct msgnum key;
struct msgnum key, **result;
key.file = f;
if (!tfind(&key, &msgnums, msgnumorder)) {
if (!(result = tfind(&key, &msgnums, msgnumorder))) {
struct msgnum *c = malloc(sizeof (struct msgnum));
c->file = f;
c->pos = line;
@ -151,9 +152,6 @@ blaze822_seq_cur(void)
int
blaze822_seq_setcur(char *s)
{
if (strcmp(s, "/dev/stdin") == 0)
return 0;
char *override = getenv("MAILDOT");
if (override)
return 0;
@ -183,7 +181,7 @@ parse_relnum(char *a, long cur, long start, long last, long *out)
if (strcmp(a, "+") == 0)
a = ".+1";
else if (strcmp(a, ".-") == 0)
else if (strcmp(a, "-") == 0)
a = ".-1";
else if (strcmp(a, ".") == 0)
a = ".+0";
@ -396,6 +394,7 @@ find_cur(char *map, struct blaze822_seq_iter *iter)
while (*s == ' ' || *s == '\t')
s++;
// printf("{%.*s}\n", t-s, s);
iter->lines++;
if (!cur && curfile &&
strncmp(s, curfile, strlen(curfile)) == 0 &&
@ -427,7 +426,7 @@ blaze822_seq_next(char *map, char *range, struct blaze822_seq_iter *iter)
if (!iter->lines) // count total lines
find_cur(map, iter);
if (!iter->s) {
if (!iter->start) {
int ret = parse_range(map, range, &iter->start, &iter->stop,
iter->cur, iter->lines);
if (ret == 1) {
@ -502,8 +501,12 @@ iterdir(char *dir, void (*cb)(char *))
long i = 0;
for (i = 0; i < n; i++) {
if (namelist[i]->d_name[0] != '.' &&
MAIL_DT(namelist[i]->d_type)) {
if (namelist[i]->d_name[0] != '.')
#if defined(DT_REG) && defined(DT_UNKNOWN)
if (namelist[i]->d_type == DT_REG ||
namelist[i]->d_type == DT_UNKNOWN)
#endif
{
snprintf(sub, sizeof sub, "%s%s/%s",
dir, m, namelist[i]->d_name);
cb(sub);
@ -536,24 +539,16 @@ blaze822_loop(int argc, char *argv[], void (*cb)(char *))
char *map = 0;
int map_opened = 0;
struct blaze822_seq_iter iter = { 0 };
int j = 0;
for (i = 0; i < argc; i++) {
if (strchr(argv[i], '/')) { // a file name
j += iterdir(argv[i], cb);
} else if (strcmp(argv[i], "-") == 0) {
if (isatty(0)) {
fprintf(stderr, "mblaze: warning: - now means "
"read mail text from standard input, "
"use .- to refer to previous mail\n");
}
cb("/dev/stdin");
j++;
} else {
if (!map_opened) {
map = blaze822_seq_open(0);
map_opened = 1;
}
struct blaze822_seq_iter iter = { 0 };
while ((line = blaze822_seq_next(map, argv[i], &iter))) {
cb(line);
free(line);

@ -1,8 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 21
plan 4
cat <<EOF >tmp
References: <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@a> <bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb@b> <ccccccccccccccccccccccccccccccc@c>
@ -16,137 +15,3 @@ check 'mime -r runs' 'mmime -r <tmp >tmp2'
check 'no overlong lines' 'awk "{if(length(\$0)>=80)exit 1}" <tmp2'
check 'no QP when unecessary' ! grep -qF "=?" tmp2
check 'no further mime necessary' 'mmime -c <tmp2'
cat <<EOF >tmp2
Subject: inclusion test
#message/rfc822 $PWD/tmp
EOF
check 'include works' 'mmime <tmp2 | grep Body'
check 'include sets filename' 'mmime <tmp2 | grep filename=tmp'
cat <<EOF >tmp2
Subject: inclusion test no filename
#message/rfc822 $PWD/tmp>
EOF
check 'include works, overriding filename' 'mmime <tmp2 | grep Disposition | grep -v filename=tmp'
cat <<EOF >tmp2
Subject: inclusion test with other disposition
#message/rfc822#inline $PWD/tmp>
EOF
check 'include works, overriding filename' 'mmime <tmp2 | grep Disposition | grep inline'
cat <<EOF >tmp2
Subject: message with content-type
Content-Type: text/plain; format=flowed
This message has format-flowed.
EOF
check 'content-type is respected if found in input' 'mmime -r <tmp2 |grep format=flowed'
cat <<EOF >tmp2
Subject: message with content-transfer-encoding
Content-Transfer-Encoding: quoted-printable
This message has already encoded. f=C3=B6=C3=B6.
EOF
check 'content-transfer-encoding is respected if found in input' 'mmime -r <tmp2 |grep f=C3=B6=C3=B6'
cat <<EOF >tmp2
Subject: message with content-type
Content-Type: text/plain; format=flowed
This message has format-flowed.
#message/rfc822 $PWD/tmp
This part too.
EOF
check 'content-type is respected if found in input, for multipart/mixed' 'mmime <tmp2 |grep format=flowed'
cat <<EOF >tmp2
Subject: message with content-transfer-encoding
Content-Transfer-Encoding: Quoted-Printable
This message has already encoded. f=C3=B6=C3=B6.
#message/rfc822 $PWD/tmp
This part too.
EOF
check 'content-transfer-encoding is respected if found in input, for multipart/mixed' 'mmime <tmp2 |grep f=C3=B6=C3=B6'
cat <<EOF >tmp2
From: Kerstin Krüger <krueger@example.com>
Body.
EOF
check 'non-ASCII is encoded as UTF-8' 'mmime <tmp2 | grep "UTF-8.*=C3=BC"'
cat <<EOF >tmp2
From: "Krüger, Kerstin" <krueger@example.com>
Body.
EOF
check 'non-ASCII quoted-strings are encoded as one encoded-word' 'mmime <tmp2 | grep "UTF-8.*=2C_"'
check 'non-ASCII quoted-strings are encoded without quotes' 'mmime <tmp2 | grep -v "=22"'
cat <<EOF >tmp2
From: "kerstin krueger"@example.com
Body.
EOF
check 'non-encoded quoted-strings are kept correctly' 'mmime <tmp2 | grep \"@'
cat <<EOF >tmp2
Subject: inclusion without further content
#message/rfc822#inline $PWD/tmp
EOF
check 'no empty parts are generated after inclusion lines' '! mmime <tmp2 | mshow -t - | grep -q size=0'
cat <<EOF >tmp2
Subject: Strict mode
Body with lines longer than 78 characters
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
EOF
check 'body lines longer than 78 characters needs MIME formatting' '! mmime -c <tmp2'
check 'MBLAZE_RELAXED_MIME allows body lines longer than 78 characters' 'MBLAZE_RELAXED_MIME= mmime -c <tmp2'
cat <<EOF >tmp2
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1710013705;
s=s1; d=tuta.io;
h=From:From:To:To:Subject:Subject:Content-Description:Content-ID:Content-Type:Content-Type:Content-Transfer-Encoding:Cc:Date:Date:In-Reply-To:MIME-Version:MIME-Version:Message-ID:Message-ID:Reply-To:References:Sender; bh=Jr8DQlZ7RwdJv94m7ZT/v+cv/WFsgjxpMRsHvnNfgGY=;
b=NXRl0YxYtVsWrR8v7tVKnvsnCSrBqqaf2h3m8OVGlzG0OqMqGcWg7fVk6x4nTYV+
+05afZrGfIwcfFwIe/LLvT0d3/12t4+cs/FQvmEcFUN+n2buQwt5sn8f76UUlvNMrGz
Xbq8HAdwhA364yWABa7DrF1EGysC8bEDJcCtSs/Wz3TL2A/MEeItEF+VijtgWUwoOwn
rFKkCg5Df+IOd4gEBS/KYLbzcMB1dvqy+ut2LA2+NZpzJQPgbJzWAYieT9KYgoS+hKS
5FfknNT+hKZz18IBEWH1UWbI+CcLRR8Sr80x2DZUKq8ryC5RmV5/uAc5Up03b/KZGRU
NsiBAQCx3w==
EOF
check 'header words longer then 78 characters do not cause empty lines (#257)' 'mmime < tmp2 | awk "NR < 5 && length == 0 { exit 1 }"'
check 'header words longer then 78 characters are printed on their own line' 'mmime < tmp2 |grep "^[ ]*h=From.*Sender;$"'

@ -1,26 +0,0 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 9
cat <<EOF >tmp
Header: foo
Header2: bar
Header-Three: quux
Header_Four: ding
Body
EOF
check_same 'Header' 'mhdr -h Header ./tmp' 'echo foo'
check_same 'Header2' 'mhdr -h Header2 ./tmp' 'echo bar'
check_same 'Header-Three' 'mhdr -h Header-Three ./tmp' 'echo quux'
check_same 'Header_Four' 'mhdr -h Header_Four ./tmp' 'echo ding'
check_same 'header' 'mhdr -h header ./tmp' 'echo foo'
check_same 'header2' 'mhdr -h header2 ./tmp' 'echo bar'
check_same 'header-Three' 'mhdr -h header-Three ./tmp' 'echo quux'
check_same 'header_Four' 'mhdr -h header_Four ./tmp' 'echo ding'
check 'issue 235' 'mhdr ./tmp |grep -i header_four'

@ -1,7 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 11
plan 10
rm -rf test.dir
mkdir test.dir
@ -12,7 +12,6 @@ mkdir -p "inbox/cur"
cat <<! | mmime >"inbox/cur/1:2,"
From: Rajwinder Kaur <rajwinder@example.com>
Obs-Test: Rajwinder Kaur <@example.org:rajwinder@example.com>
Subject: namaste
Date: Thu, 30 Mar 2017 15:42:05 +0200
Message-Id: <EOH1F3NUOY.2KBVMHSBFATNY@example.org>
@ -20,7 +19,7 @@ Message-Id: <EOH1F3NUOY.2KBVMHSBFATNY@example.org>
!
cat <<! | mmime >"inbox/cur/2:2,"
From: имярек <namearek@example.com>, Rajwinder Kaur <rajwinder@example.com>
From: имярек <имярек@example.com>, Rajwinder Kaur <rajwinder@example.com>
Subject: Здравствуйте
Date: Thu, 30 Mar 2017 15:42:10 +0200
Message-Id: <EOH8EKEA1Z.2YAIFN5KVCO6Z@example.org>
@ -67,10 +66,9 @@ export MAILSEQ=seq
check_same 'from one' 'maddr 1' 'echo "Rajwinder Kaur <rajwinder@example.com>"'
check_same 'from address' 'maddr -a 1' 'echo "rajwinder@example.com"'
check_same 'from one' 'maddr -h obs-test 1' 'echo "Rajwinder Kaur <rajwinder@example.com>"'
cat <<! >expect
имярек <namearek@example.com>
имярек <имярек@example.com>
Rajwinder Kaur <rajwinder@example.com>
!
check_same 'from two' 'maddr 2' 'cat expect'

@ -1,10 +1,10 @@
#!/bin/sh
cd ${0%/*}
. ./lib.sh
plan 33
plan 27
check_addr() {
printf "From: %s\n" "$1" | check_test "parse $1" = "$2" "maddr -"
printf "From: %s\n" "$1" | check_test "parse $1" = "$2" "maddr /dev/stdin"
}
check_addr 'foo@example.org' 'foo@example.org'
@ -41,11 +41,3 @@ check_addr 'foo@[::1] (ipv6)' 'ipv6 <foo@[::1]>'
check_addr '<Foo Bar <foobar@qux.com>' 'foobar@qux.com'
check_addr '"abc@def"@ghi' ''
check_addr '"foo@" <bar.com foo@bar.com>' '"foo@" <bar.comfoo@bar.com>'
check_addr 'test."test"@example.org' 'test.test@example.org'
check_addr '<test."test"@example.org>' 'test.test@example.org'
check_addr 'test"test"@example.org' 'testtest@example.org'
check_addr '<test"test"@example.org>' 'testtest@example.org'
check_addr 'foo<bar@example.org>' 'foo <bar@example.org>'
check_addr 'xxxxxxxxx a"test"@example.org' "xxxxxxxxx <atest@example.org>"

@ -1,41 +0,0 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 2
# Nested MIME where the outer boundary is a prefix of the inner boundary
cat <<EOF >tmp
MIME-Version: 1.0
Content-type: multipart/mixed; charset=iso-8859-1;
boundary="_xec5AqfRYxfhARmklHx"
--_xec5AqfRYxfhARmklHx
Content-type: Multipart/alternative; charset=iso-8859-1;
boundary="_xec5AqfRYxfhARmklHx8"
--_xec5AqfRYxfhARmklHx8
Content-type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
foo
--_xec5AqfRYxfhARmklHx8
Content-type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: Quoted-printable
Content-Disposition: inline
bar
--_xec5AqfRYxfhARmklHx8--
--_xec5AqfRYxfhARmklHx
Content-Type: application/zip
Content-Transfer-Encoding: Base64
quux
--_xec5AqfRYxfhARmklHx--
EOF
check 'nested mail has 5 attachments' 'mshow -t ./tmp | wc -l | grep 6'
check 'nested mail has text/html attachment' 'mshow -t ./tmp | grep text/html'

@ -1,53 +0,0 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 3
# Mail with \n\n and \r\n\r\n
cr=$(printf '\r')
cat <<EOF >tmp
Content-Type: multipart/form-data; boundary=------------------------55a586f81559face$cr
$cr
--------------------------55a586f81559face$cr
Content-Disposition: form-data; name="a"; filename="foo"$cr
Content-Type: application/octet-stream$cr
$cr
foo$cr
previously there are two NL$cr
$cr
--------------------------55a586f81559face$cr
Content-Disposition: form-data; name="a"; filename="bar"$cr
Content-Type: application/octet-stream$cr
$cr
bar$cr
$cr
--------------------------55a586f81559face--$cr
EOF
check 'mail has 3 attachments' 'mshow -t ./tmp | wc -l | grep 4'
check 'mail attachment foo has size 35' 'mshow -t ./tmp | grep size=35.*name=\"foo\"'
# Mail with linebreaks in base64 quartets
cat <<EOF >tmp
Subject: base64
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----_=_2f8f1e2243b55f8618eaf0d9_=_"
This is a multipart message in MIME format.
------_=_2f8f1e2243b55f8618eaf0d9_=_
Content-Disposition: attachment; filename=base64
Content-Type: application/binary
Content-Transfer-Encoding: base64
dGhp
cyBpc
yBzb21
lIGJhc2
U2NAo=
------_=_2f8f1e2243b55f8618eaf0d9_=_--
EOF
check 'mail decodes correctly' 'mshow -O ./tmp 2 | grep -q "this is some base64"'

@ -1,26 +0,0 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 2
cat <<EOF >tmp.1
Subject: message 1
This is message 1.
EOF
cat <<EOF >tmp.2
Subject: message 2
This is message 2. It has a trailing empty line.
EOF
printf >tmp.3 'Subject: message 3
This is message 3. It has a no trailing newline, oops.'
mexport ./tmp.1 ./tmp.2 ./tmp.3 >./tmp.mbox
check 'generated mbox has 16 lines' 'cat ./tmp.mbox | wc -l | grep 16'
check 'generated mbox has 7 empty lines' 'grep -c "^$" ./tmp.mbox | grep 7'

@ -1,85 +0,0 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 9
rm -rf test.dir
mkdir test.dir
cd test.dir
mmkdir inbox
cat <<EOF >tmp.1
Subject: message 1
This is message 1.
EOF
cat <<EOF >tmp.2
Subject: message 2
This is message 2. It has a trailing empty line.
EOF
printf >tmp.3 'Subject: message 3
This is message 3. It has a no trailing newline, oops.'
cat <<EOF >tmp.4
Subject: message 4
This is message 4. It has multiple trailing empty lines.
EOF
mexport ./tmp.1 | mdeliver -M inbox/
check 'message 1 is delivered verbatim via mbox' 'cmp ./tmp.1 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mexport ./tmp.2 | mdeliver -M inbox/
check 'message 2 is delivered verbatim via mbox' 'cmp ./tmp.2 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mexport ./tmp.3 | mdeliver -M inbox/
check 'message 3 gets a newline via mbox' 'awk 1 ./tmp.3 | cmp - ./inbox/new/*:2,'
rm -f ./inbox/new/*
mexport ./tmp.4 | mdeliver -M inbox/
check 'message 4 gets delivered verbatim via mbox' 'cmp ./tmp.4 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.1
check 'message 1 is delivered verbatim via stdin' 'cmp ./tmp.1 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.2
check 'message 2 is delivered verbatim via stdin' 'cmp ./tmp.2 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.3
check 'message 3 gets a newline via stdin' 'cmp ./tmp.3 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.4
check 'message 4 is delivered verbatim via stdin' 'cmp ./tmp.4 ./inbox/new/*:2,'
rm -f ./inbox/new/*
cat <<EOF >tmp.mbox
From nobody Thu Jan 1 00:59:59 1970
Subject: message 1
This is message 1.
From nobody Thu Jan 1 00:59:59 1970
Subject: message 2
This is message 2.
EOF
mdeliver -M inbox/ <./tmp.mbox
check 'mdeliver -M is tolerant with missing empty lines' 'ls inbox/new | wc -l | grep 2'

@ -1,7 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 29
plan 13
rm -rf test.dir
mkdir test.dir
@ -19,14 +19,14 @@ touch "inbox/cur/7:2,SR"
touch "inbox/cur/8:2,SF"
touch "inbox/cur/9:2,"
check_same 'flag trashed' 'mlist inbox | mpick -t trashed' 'mlist -T inbox'
check_same 'flag not trashed' 'mlist inbox | mpick -t !trashed' 'mlist -t inbox'
check_same 'flag seen' 'mlist inbox | mpick -t seen' 'mlist -S inbox'
check_same 'flag trashed' 'mlist inbox | mpick :T' 'mlist -T inbox'
check_same 'flag not trashed' 'mlist inbox | mpick -t "!trashed"' 'mlist -t inbox'
check_same 'flag seen' 'mlist inbox | mpick :S' 'mlist -S inbox'
check_same 'flag not seen' 'mlist inbox | mpick -t !seen' 'mlist -s inbox'
check_same 'flag seen and trashed' 'mlist inbox | mpick -t seen -t trashed' 'mlist -ST inbox'
check_same 'flag seen and not trashed' 'mlist inbox | mpick -t seen -t !trashed' 'mlist -St inbox'
check_same 'flag replied' 'mlist inbox | mpick -t replied' 'mlist -R inbox'
check_same 'flag forwarded' 'mlist inbox | mpick -t passed' 'mlist -P inbox'
check_same 'flag seen and trashed' 'mlist inbox | mpick :S :T' 'mlist -ST inbox'
check_same 'flag seen and not trashed' 'mlist inbox | mpick -t "seen && !trashed"' 'mlist -St inbox'
check_same 'flag replied' 'mlist inbox | mpick :R' 'mlist -R inbox'
check_same 'flag forwarded' 'mlist inbox | mpick :F' 'mlist -F inbox'
cat <<! | mmime >"inbox/cur/1:2,S"
@ -61,75 +61,10 @@ Greetings
#application/pdf ../../mshow
!
cat <<! >shebang
#!$(command -v mpick) -F
from.addr == "peter@example.org" && from.disp == "Peter Example"
!
chmod +x shebang
check 'search subject' 'mlist inbox | mpick -t "subject =~~ \"wow\"" | grep -q inbox/cur/9:2,'
check_test 'search addr' -eq 2 'mlist inbox | mpick -t "from.addr == \"peter@example.org\"" | wc -l'
check_test 'search name' -eq 2 'mlist inbox | mpick -t "from.disp == \"Peter Example\"" | wc -l'
check 'search subject' 'mlist inbox | mpick /wow | grep -q inbox/cur/9:2,'
check_test 'search addr' -eq 2 'mlist inbox | mpick peter@example.org | wc -l'
check_test 'search name' -eq 2 'mlist inbox | mpick "Peter Example" | wc -l'
check_test 'search spam' -eq 1 'mlist inbox | mpick -t "trashed && subject =~ \"pdf\"" | wc -l'
check_test 'any header' -eq 1 'mlist inbox | mpick -t "\"Foo\" =~~ \"bar\"" | wc -l'
check_test 'addr decode addr' -eq 2 'mlist inbox | mpick -t "from.addr == \"peter@example.org\"" | wc -l'
check_test 'addr decode disp' -eq 2 'mlist inbox | mpick -t "from.disp == \"Peter Example\"" | wc -l'
check_test 'shebang' -eq 2 'mlist inbox | ./shebang | wc -l'
check_test 'ternary' -eq 2 'mlist inbox | mpick -t "from.addr == \"peter@example.org\" ? print : skip" | wc -l'
check_same 'pipe command' 'mlist inbox | mpick -t "print |\"cat -n\" && skip"' 'mlist inbox | cat -n'
check_same 'create file' 'mlist inbox | mpick -t "print >\"foo\" && skip" && cat foo' 'mlist inbox'
check_same 'overwrite file' 'mlist inbox | mpick -t "print >\"foo\" && skip" && cat foo' 'mlist inbox'
check_same 'append file' 'mlist inbox | mpick -t "print >>\"foo\" && skip" && cat foo' 'mlist inbox && mlist inbox'
check_same 'unknown ident' 'mlist inbox | mpick -t "let x = x in x" 2>&1' \
"echo \"mpick: parse error: argv:1:9: unknown expression at 'x in x'\""
cat <<! >expr
let foo = from.addr == "peter@example.org"
let bar = from.disp == "Peter Example"
# random comment
in
foo && bar # another comment
!
check_test 'let expression' -eq 2 'mlist inbox | mpick -F ./expr | wc -l'
cat <<! >expr
let foo = from.addr == "peter@example.org"
let bar = from.disp == "Peter Example"
# random comment
in
foo && foo
!
check_test 'let expression double free' -eq 2 'mlist inbox | mpick -F ./expr | wc -l'
cat <<! >expr
let foo =
let bar = from.disp == "Peter Example"
in
bar && from.addr == "peter@example.org"
in
foo
!
check_test 'let expression nested' -eq 2 'mlist inbox | mpick -F ./expr | wc -l'
cat <<! >expr
let foo = from.addr == "peter@example.org"
let bar = foo && subject =~ "wow"
in
bar
!
check_test 'let scoping' -eq 1 'mlist inbox | mpick -F ./expr | wc -l'
cat <<! >expr
let foo = from.addr == "peter@example.org"
let bar = from.disp == "Peter Example"
in
foo |"sed ""s/^/1:&/""" && bar |"sed ""s/^/2:&/""" && skip
!
check_test 'multi redir' -eq 4 'mlist inbox | mpick -F ./expr | wc -l'
check_test 'multi redir prefixes' -eq 2 'mlist inbox | mpick -F ./expr | cut -d: -f1 | sort -u | wc -l'
)
check 'environment variable' 'mlist inbox | MSGID="<EOH1F3NUOY.2KBVMHSBFATNY@example.org>" mpick -t "\"message-id\" == \$MSGID"'

@ -172,23 +172,23 @@ inbox/cur/4:2,
export MAILSEQ=seq
check_test 'subject' -eq 1 'magrep subject:nice : | wc -l'
check_test 'ignorecase' -eq 1 'magrep -i subject:NICE : | wc -l'
check_test 'invert' -eq 2 'magrep -v subject:nice : | wc -l'
check_test 'max matches' -eq 2 'magrep -m 2 from:Piet : | wc -l'
check_test 'long subject' -eq 1 'magrep subject:aliqua : | wc -l'
check_test 'decode large rfc2047 header' -eq 1 'magrep -d to:John : | wc -l'
check_test 'subject' -eq 1 'magrep subject:nice | wc -l'
check_test 'ignorecase' -eq 1 'magrep -i subject:NICE | wc -l'
check_test 'invert' -eq 2 'magrep -v subject:nice | wc -l'
check_test 'max matches' -eq 2 'magrep -m 2 from:Piet | wc -l'
check_test 'long subject' -eq 1 'magrep subject:aliqua | wc -l'
check_test 'decode large rfc2047 header' -eq 1 'magrep -d to:John | wc -l'
echo 'inbox/cur/1:2,: subject: wow nice subject' >expect
check_same 'print' 'magrep -p subject:nice :' 'cat expect'
check_same 'print' 'magrep -p subject:nice' 'cat expect'
echo 'inbox/cur/1:2,: subject: nice' >expect
check_same 'print match' 'magrep -po subject:nice :' 'cat expect'
check_same 'print match' 'magrep -po subject:nice' 'cat expect'
echo 'nice' >expect
check_same 'print match only' 'magrep -o subject:nice :' 'cat expect'
check_same 'print match only' 'magrep -o subject:nice' 'cat expect'
echo 'inbox/cur/3:2,' >expect
check_same 'multiple subjects' 'magrep subject:multi :' 'cat expect'
check_same 'multiple subjects' 'magrep subject:multi' 'cat expect'
)

@ -1,166 +0,0 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 12
rm -rf test.dir
mkdir test.dir
(
cd test.dir
export PIPE_CONTENTTYPE='text/plain; format=flowed'
export COLUMNS=80
cat <<! >a
this
is
flowed.
!
cat <<! >b
this is flowed.
!
check 'simple reflow' 'mflow <a | cmp - b'
cat <<! >a
this
is
two spaces.
!
cat <<! >b
this is two spaces.
!
check 'simple space stuffing' 'mflow <a | cmp - b'
cat <<! >a
this
is
flowed.
this is fixed.
!
cat <<! >b
this is flowed.
this is fixed.
!
check 'simple fixed' 'mflow <a | cmp - b'
cat <<! >a
> this
> is
> quoted.
this
is
unquoted.
!
cat <<! >b
> this is quoted.
this is unquoted.
!
check 'simple quoted' 'mflow <a | cmp - b'
(
export PIPE_CONTENTTYPE='text/plain; format=flowed; delsp=yes'
cat <<! >a
> this
> is
> delsp.
> double
> spaced
!
cat <<! >b
> thisisdelsp.
> double spaced
!
check 'simple delsp' 'mflow <a | cmp - b'
)
cat <<! >a
this
is
way more than eighty chars which is the terminal width to flow.
this
is
way more than eighty chars which is the terminal width to flow.
!
cat <<! >b
this is way more than eighty chars which is the terminal width to flow. this is
way more than eighty chars which is the terminal width to flow.
!
check 'simple wrap' 'mflow <a | cmp - b'
cat <<! >a
this
is
way more than eighty chars which is the terminal width to flow.
averylongwordcomeshere.
this
is
way more than eighty chars which is the terminal width to flow.
!
cat <<! >b
this is way more than eighty chars which is the terminal width to flow.
averylongwordcomeshere. this is way more than eighty chars which is the
terminal width to flow.
!
check 'more complex wrap' 'mflow <a | cmp - b'
cat <<! >a
foo
bar.
quux.
!
cat <<! >b
foo bar.
quux.
!
check 'space before empty line' 'mflow <a | cmp - b'
cat <<! >a
Aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee
aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee
aaaaa bbbbb ccccc
ffffff gggggg hhhhhh iiiiii.
!
cat <<! >b
Aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc
ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ffffff gggggg
hhhhhh iiiiii.
!
check 'fixed lines are wrapped too' 'mflow <a | cmp - b'
cat <<! >a
some
wrapped.
--
signature
!
cat <<! >b
some wrapped.
--
signature
!
check 'passes usenet signature marker as is' 'mflow <a | cmp - b'
cat <<! >a
some regular text being force wrapped because the line is way too long oh no who writes so long lines.
!
cat <<! >b
some regular text being force wrapped because the line is way too long oh no
who writes so long lines.
!
check 'force wrapping' 'mflow -f <a | cmp - b'
cat <<! >a
> some regular text being force wrapped because the line is way too long oh no who writes so long lines.
!
cat <<! >b
> some regular text being force wrapped because the line is way too long oh no
> who writes so long lines.
!
check 'force wrapping of quoted text' 'mflow -f <a | cmp - b'
)

@ -1,7 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 3
plan 2
rm -rf test.dir
mkdir test.dir
@ -21,13 +21,11 @@ body
cat <<! >seq
inbox/cur/1:2,
inbox/cur/1:2,
!
export MAILSEQ=seq
check_same 'ISO date' 'TZ=utc mscan -f "%16D" 1' 'echo "2017-03-30 13:42"'
check_same 'from name' 'mscan -f "%f" 1' 'echo "Rajwinder Kaur"'
check_test 'multiple mmsg' -eq 2 'mscan 1 1 | wc -l'
)

@ -51,7 +51,7 @@ inbox/cur/4:2,
export MAILSEQ=seq
check_same 'filename' 'msort -F :' 'cat seq'
check_same 'filename' 'msort -F' 'cat seq'
cat <<! >expect
inbox/cur/3:2,
@ -59,7 +59,7 @@ inbox/cur/1:2,
inbox/cur/2:2,
inbox/cur/4:2,
!
check_same 'date' 'msort -d :' 'cat expect'
check_same 'date' 'msort -d' 'cat expect'
cat <<! >expect
inbox/cur/4:2,
@ -67,7 +67,7 @@ inbox/cur/2:2,
inbox/cur/1:2,
inbox/cur/3:2,
!
check_same 'reverse date' 'msort -dr :' 'cat expect'
check_same 'reverse date' 'msort -dr' 'cat expect'
cat <<! >expect
inbox/cur/2:2,
@ -75,7 +75,7 @@ inbox/cur/3:2,
inbox/cur/4:2,
inbox/cur/1:2,
!
check_same 'from' 'msort -f :' 'cat expect'
check_same 'from' 'msort -f' 'cat expect'
cat <<! >expect
inbox/cur/3:2,
@ -83,6 +83,6 @@ inbox/cur/2:2,
inbox/cur/4:2,
inbox/cur/1:2,
!
check_same 'subject' 'msort -s :' 'cat expect'
check_same 'subject' 'msort -s' 'cat expect'
)

@ -1,7 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 11
plan 10
rm -rf test.dir
mkdir test.dir
@ -25,13 +25,12 @@ export MAILCUR=cur MAILSEQ=seq
check 'set current' 'mseq -C 1 && mseq . | grep "inbox/cur/1:2,"'
check 'set next' 'mseq -C + && mseq . | grep "inbox/cur/2:2,"'
check 'set prev' 'mseq -C .- && mseq . | grep "inbox/cur/1:2,"'
check 'set prev' 'mseq -C - && mseq . | grep "inbox/cur/1:2,"'
check 'last' 'mseq "$" | grep "inbox/cur/10:2,"'
check_test 'whole thread' -eq 4 'mseq 6= | wc -l'
check_test 'subthread' -eq 2 'mseq 7_ | wc -l'
check 'parent' 'mseq 6^ | grep "inbox/cur/5_1:2,"'
check_test 'range' -eq 3 'mseq 1:3 | wc -l'
check_same 'multiple mmsg' 'mseq 1 2' 'printf "inbox/cur/1:2,\ninbox/cur/2:2,\n"'
cat <<! >seq
inbox/cur/1:2,

@ -20,20 +20,20 @@ ln -sf inbox/cur/1:2, cur
export MAILSEQ=seq MAILCUR=cur
check 'mark seen' 'mflag -S 1 && [ -e "inbox/cur/1:2,S" ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'mark replied' 'mflag -R 1 && [ -e "inbox/cur/1:2,RS" ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'unmark replied' 'mflag -r 1 && [ -e "inbox/cur/1:2,S" ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'mark flagged' 'mflag -F 1 && [ -e "inbox/cur/1:2,FS" ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'unmark flagged' 'mflag -f 1 && [ -e "inbox/cur/1:2,S" ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'unmark seen' 'mflag -s 1 && [ -e "inbox/cur/1:2," ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'mark trashed' 'mflag -T 1 && [ -e "inbox/cur/1:2,T" ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check 'unmark trashed' 'mflag -t 1 && [ -e "inbox/cur/1:2," ]'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
)

@ -1,7 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 2
plan 1
rm -rf test.dir
mkdir test.dir
@ -16,10 +16,4 @@ inbox/new/2
check_test 'minc' -eq 2 'minc inbox | wc -l'
while read f; do touch "$f"; done <<!
inbox/new/3:2,
inbox/new/4
!
check_test 'minc stdin' -eq 2 'echo inbox | minc | wc -l'
)

@ -28,8 +28,8 @@ check_test() {
check_same() {
msg=$1
shift
eval "$1 || true" 2>/dev/null 1>&2 >out1 || true
eval "$2 || true" 2>/dev/null 1>&2 >out2 || true
eval "$1" 2>/dev/null 1>&2 >out1 || true
eval "$2" 2>/dev/null 1>&2 >out2 || true
diff -u out1 out2 || true
check "$msg" cmp out1 out2
}

@ -1,26 +0,0 @@
#ifndef PLEDGE_H
#define PLEDGE_H
#ifdef __OpenBSD__
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#include <err.h>
#include <unistd.h>
static void
xpledge(const char *promises, const char *execpromises)
{
if (pledge(promises, execpromises) == -1)
err(1, "pledge");
}
#else
#define xpledge(promises, execpromises) do { } while(0)
#endif /* __OpenBSD__ */
#endif /* PLEDGE_H */
Loading…
Cancel
Save