mirror of https://github.com/leahneukirchen/mblaze
add mflow
parent
1382543ae8
commit
9387d1fe07
@ -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…
Reference in New Issue