add mflow

pull/69/head
Leah Neukirchen 7 years ago
parent 1382543ae8
commit 9387d1fe07

@ -17,18 +17,18 @@ PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
MANDIR=$(PREFIX)/share/man
ALL = maddr magrep mdate mdeliver mdirs mexport mflag mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread
ALL = maddr magrep mdate mdeliver mdirs mexport mflag mflow mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread
SCRIPT = mcolor mcom mless mmkdir mquote museragent
all: $(ALL) museragent
$(ALL) : % : %.o
maddr magrep mdeliver mexport mflag mgenmid mhdr mpick mscan msed mshow \
maddr magrep mdeliver mexport mflag mflow mgenmid mhdr mpick mscan msed mshow \
msort mthread : blaze822.o mymemmem.o mytimegm.o
maddr magrep mexport mflag mgenmid mhdr mlist mpick mscan msed mseq mshow msort \
mthread : seq.o slurp.o
maddr magrep mhdr mpick mscan mshow : rfc2047.o
magrep mshow : rfc2045.o
maddr magrep mflow mhdr mpick mscan mshow : rfc2047.o
magrep mflow mshow : rfc2045.o
mshow : filter.o safe_u8putstr.o rfc2231.o pipeto.o
mscan : pipeto.o
msort : mystrverscmp.o

@ -18,6 +18,7 @@ DESCRIPTION
mdirs(1) find Maildir folders
mexport(1) export Maildir folders as mailboxes
mflag(1) change flags (marks) of mail
mflow(1) reflow format=flowed plain text mails
mfwd(1) forward mail
mgenmid(1) generate Message-IDs
mhdr(1) extract mail headers

@ -30,6 +30,8 @@ find Maildir folders
export Maildir folders as mailboxes
.It Xr mflag 1
change flags (marks) of mail
.It Xr mflow 1
reflow format=flowed plain text mails
.It Xr mfwd 1
forward mail
.It Xr mgenmid 1

@ -0,0 +1,55 @@
.Dd July 26, 2017
.Dt MFLOW 1
.Os
.Sh NAME
.Nm mflow
.Nd reflow format=flowed plain text mails
.Sh SYNOPSIS
.Nm
\&<
.Ar file
.Sh DESCRIPTION
.Nm
reformats the standard input according to the rules
of RFC 3676.
.Ev PIPE_CONTENTTYPE
is inspected, making this a suitable filter
for
.Sq text/plain
messages for
.Xr mshow 1 .
Mails not using
.Sq format=flowed
are output as is.
.Pp
Text is reflowed (where allowed) to
fit the width given in the environment variable
.Ev COLUMNS ,
the terminal width, or 80 characters by default.
.Pp
If defined,
the environment variable
.Ev MAXCOLUMNS
specifies the maximum line length.
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr mshow 1
.Rs
.%A R. Gellens
.%D February 2004
.%R RFC 3676
.%T The Text/Plain Format and DelSp Parameters
.Re
.Sh AUTHORS
.An Leah Neukirchen Aq Mt leah@vuxu.org
.Sh LICENSE
.Nm
is in the public domain.
.Pp
To the extent possible under law,
the creator of this work
has waived all copyright and related or
neighboring rights to this work.
.Pp
.Lk http://creativecommons.org/publicdomain/zero/1.0/

@ -0,0 +1,187 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "blaze822.h"
int column = 0;
int maxcolumn = 80;
void
chgquote(int quotes)
{
static int oquotes;
if (quotes != oquotes) {
if (column)
putchar('\n');
column = 0;
oquotes = quotes;
}
}
void
fixed(int quotes, char *line, size_t linelen)
{
chgquote(quotes);
if (linelen > (size_t)(maxcolumn - column)) {
putchar('\n');
column = 0;
}
if (column == 0) {
for (; column < quotes; column++)
putchar('>');
if (quotes)
putchar(' ');
}
fwrite(line, 1, linelen, stdout);
putchar('\n');
column = 0;
}
void
flowed(int quotes, char *line, ssize_t linelen)
{
chgquote(quotes);
int done = 0;
while (!done) {
if (column == 0) {
for (; column < quotes; column++)
putchar('>');
column++;
if (quotes)
putchar(' ');
}
char *eow;
if (*line == ' ')
eow = memchr(line + 1, ' ', linelen - 1);
else
eow = memchr(line, ' ', linelen);
if (!eow) {
eow = line + linelen;
done = 1;
}
if (column + (eow - line) > maxcolumn) {
putchar('\n');
column = 0;
if (*line == ' ') {
line++;
linelen--;
}
} else {
fwrite(line, 1, eow - line, stdout);
column += eow - line;
linelen -= eow - line;
line = eow;
}
}
}
int
main()
{
char *linebuf = 0;
char *line;
size_t linelen = 0;
int quotes = 0;
int reflow = 1; // re-evaluated on $PIPE_CONTENTTYPE
int delsp = 0;
char *ct = getenv("PIPE_CONTENTTYPE");
if (ct) {
char *s, *se;
blaze822_mime_parameter(ct, "format", &s, &se);
reflow = s && (strncasecmp(s, "flowed", 6) == 0);
blaze822_mime_parameter(ct, "delsp", &s, &se);
delsp = s && (strncasecmp(s, "yes", 3) == 0);
}
char *cols = getenv("COLUMNS");
if (cols && isdigit(*cols)) {
maxcolumn = atoi(cols);
} else {
struct winsize w;
int fd = open("/dev/tty", O_RDONLY | O_NOCTTY);
if (fd >= 0) {
if (ioctl(fd, TIOCGWINSZ, &w) == 0)
maxcolumn = w.ws_col;
close(fd);
}
}
char *maxcols = getenv("MAXCOLUMNS");
if (maxcols && isdigit(*maxcols)) {
int m = atoi(maxcols);
if (maxcolumn > m)
maxcolumn = m;
}
while (1) {
errno = 0;
ssize_t rd = getdelim(&linebuf, &linelen, '\n', stdin);
if (rd == -1) {
if (errno == 0)
break;
fprintf(stderr, "mflow: error reading: %s\n",
strerror(errno));
exit(1);
}
line = linebuf;
if (!reflow) {
fwrite(line, 1, rd, stdout);
continue;
}
if (rd > 0 && line[rd-1] == '\n')
line[--rd] = 0;
if (rd > 0 && line[rd-1] == '\r')
line[--rd] = 0;
quotes = 0;
while (*line == '>') { // measure quote depth
line++;
quotes++;
rd--;
}
if (*line == ' ') { // space stuffing
line++;
rd--;
}
if (strcmp(line, "-- ") == 0) { // usenet signature convention
if (column)
fixed(quotes, "", 0); // flush paragraph
fixed(quotes, line, rd);
continue;
}
if (rd > 0 && line[rd-1] == ' ') { // flowed line
if (delsp)
line[--rd] = 0;
flowed(quotes, line, rd);
} else {
fixed(quotes, line, rd);
}
}
if (reflow && column != 0)
putchar('\n');
}
Loading…
Cancel
Save