aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-07-26 17:23:01 +0200
committerLeah Neukirchen <leah@vuxu.org>2017-07-26 17:23:01 +0200
commit9387d1fe07fc34c3015d4bcda466e93490ec3868 (patch)
treeb37416adfad9b3c4546716b2f61baff41be93edb
parent1382543ae8c2db437e7c8c69c3e45bafc4d9eba3 (diff)
downloadmblaze-9387d1fe07fc34c3015d4bcda466e93490ec3868.tar.gz
add mflow
-rw-r--r--GNUmakefile8
-rw-r--r--README1
-rw-r--r--man/mblaze.72
-rw-r--r--man/mflow.155
-rw-r--r--mflow.c187
5 files changed, 249 insertions, 4 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 5f1d6a6..9430a18 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -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
diff --git a/README b/README
index b74dc45..d228edb 100644
--- a/README
+++ b/README
@@ -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
diff --git a/man/mblaze.7 b/man/mblaze.7
index 13b37d0..d843968 100644
--- a/man/mblaze.7
+++ b/man/mblaze.7
@@ -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
diff --git a/man/mflow.1 b/man/mflow.1
new file mode 100644
index 0000000..63a2a23
--- /dev/null
+++ b/man/mflow.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/
diff --git a/mflow.c b/mflow.c
new file mode 100644
index 0000000..56af515
--- /dev/null
+++ b/mflow.c
@@ -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');
+}