Here is my simple line folding filter.
Going with getc() is a lot simpler but this way seems faster and it was fun to play around with. And it seems to work.
This will break long lines exactly at the specified length without regard to whitespace. In the header, a tab (\t) is added after added newlines.
This would work for e.g. attachments formatted as one very long line of base64. It could cause problems in other cases.
compile: gcc -o email-line-fold email-line-fold.c
usage:
remote_smtp:
transport_filter = '${if > {$max_received_linelength}{998}{/usr/local/bin/email-line-fold}{}}' 998
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <error.h>
#define BUFSIZE 4096
int main(int argc, char *argv[])
{
char *endptr, *str;
long linemax = 0;
char buf[BUFSIZE];
char *bufi;
char *bufend;
char *nl;
ssize_t bytes_read = 0;
ssize_t bytes_written = 0;
size_t bytes_remaining = 0;
size_t bytestowrite = 0;
int header_done = 0;
int length = 0;
int lastlength = 0;
if (argc < 2) {
fprintf(stderr, "Usage: %s linemax\n", argv[0]);
exit(EXIT_FAILURE);
}
str = argv[1];
errno = 0;
linemax = strtol(str, &endptr, 0);
if (endptr == str) {
fprintf(stderr, "Usage: %s linemax\n", argv[0]);
fprintf(stderr, "linemax must be a numerical value\n");
exit(EXIT_FAILURE);
}
if (errno != 0) {
perror("strtol");
exit(EXIT_FAILURE);
}
if (linemax < 2) {
fprintf(stderr, "linemax must be at least 2\n");
exit(EXIT_FAILURE);
}
bufi = &buf[0];
while (1) {
if (! bytes_remaining) {
if (feof(stdin)) break;
bytes_read = fread(buf, 1, BUFSIZE, stdin);
if (ferror (stdin))
error(EXIT_FAILURE, errno, "Error while reading");
if (! bytes_read) break;
bytes_remaining = bytes_read;
bufi = &buf[0];
bufend = &buf[0] + bytes_read - 1;
}
if (! header_done && nl == bufend && buf[0] == '\n')
header_done = 1;
while (bytes_remaining) {
nl = memchr(bufi, '\n', bytes_remaining);
if (nl == NULL)
length = bytes_remaining;
else
length = nl - bufi + 1;
if (! header_done && nl != NULL && nl < bufend && nl[1] == '\n')
header_done = 1;
bytes_remaining -= length;
while (length) {
int addnl = 0;
if (lastlength + length > linemax) {
bytestowrite = linemax - lastlength;
if (nl == NULL || lastlength + length > (linemax + 1))
addnl = 1;
lastlength = 0;
} else {
bytestowrite = length;
if (bufi[length-1] != '\n')
lastlength += length;
else
lastlength = 0;
}
bytes_written = fwrite(bufi, 1, bytestowrite, stdout);
if (ferror (stdout))
error(EXIT_FAILURE, errno, "Error while writing");
if (addnl)
if (! header_done) {
if (fputs("\n\t", stdout) == EOF)
error(EXIT_FAILURE, errno, "Error while writing");
lastlength++;
}
else {
if (putchar('\n') == EOF)
error(EXIT_FAILURE, errno, "Error while writing");
}
length -= bytestowrite;
bufi += bytestowrite;
}
}
}
fclose(stdin);
fclose(stdout);
return 0;
}