I'd forgotten that adding a "charset" specification to the Content-Type header is also needed. In the *new* set of patches below, besides I corrected some other errors, I added a '-y' option to specify utf-8 character set:
$ mail -s Hello -e quoted-printable -y "text/plain;charset=utf-8" \ recipi...@example.com < message.txt Index: collect.c =================================================================== RCS file: /cvs/src/usr.bin/mail/collect.c,v retrieving revision 1.34 diff -u -p -r1.34 collect.c --- collect.c 17 Jan 2014 18:42:30 -0000 1.34 +++ collect.c 19 Sep 2023 13:30:14 -0000 @@ -87,7 +87,7 @@ collect(struct header *hp, int printhead * refrain from printing a newline after * the headers (since some people mind). */ - t = GTO|GSUBJECT|GCC|GNL; + t = GTO|GSUBJECT|GENCODING|GTYPE|GCC|GNL; getsub = 0; if (hp->h_subject == NULL && value("interactive") != NULL && (value("ask") != NULL || value("asksub") != NULL)) @@ -208,7 +208,7 @@ cont: /* * Grab a bunch of headers. */ - grabh(hp, GTO|GSUBJECT|GCC|GBCC); + grabh(hp, GTO|GSUBJECT|GENCODING|GTYPE|GCC|GBCC); goto cont; case 't': /* @@ -328,7 +328,7 @@ cont: */ rewind(collf); puts("-------\nMessage contains:"); - puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); + puthead(hp, stdout, GTO|GSUBJECT|GENCODING|GTYPE|GCC|GBCC|GNL); while ((t = getc(collf)) != EOF) (void)putchar(t); goto cont; Index: def.h =================================================================== RCS file: /cvs/src/usr.bin/mail/def.h,v retrieving revision 1.17 diff -u -p -r1.17 def.h --- def.h 28 Jan 2022 06:18:41 -0000 1.17 +++ def.h 19 Sep 2023 13:30:14 -0000 @@ -158,12 +158,14 @@ struct headline { #define GSUBJECT 2 /* Likewise, Subject: line */ #define GCC 4 /* And the Cc: line */ #define GBCC 8 /* And also the Bcc: line */ -#define GMASK (GTO|GSUBJECT|GCC|GBCC) +#define GMASK (GTO|GSUBJECT|GENCODING|GTYPE|GCC|GBCC) /* Mask of places from whence */ #define GNL 16 /* Print blank line after */ #define GDEL 32 /* Entity removed from list */ #define GCOMMA 64 /* detract puts in commas */ +#define GENCODING 128 /* Content-Transfer-Encoding: line */ +#define GTYPE 256 /* Content-Type: line */ /* * Structure used to pass about the current @@ -173,6 +175,8 @@ struct header { struct name *h_to; /* Dynamic "To:" string */ char *h_from; /* User-specified "From:" string */ char *h_subject; /* Subject string */ + char *h_encoding; /* Content-Transfer-Encoding string */ + char *h_type; /* Content-Type string */ struct name *h_cc; /* Carbon copies string */ struct name *h_bcc; /* Blind carbon copies */ struct name *h_smopts; /* Sendmail options */ Index: extern.h =================================================================== RCS file: /cvs/src/usr.bin/mail/extern.h,v retrieving revision 1.29 diff -u -p -r1.29 extern.h --- extern.h 16 Sep 2018 02:38:57 -0000 1.29 +++ extern.h 19 Sep 2023 13:30:14 -0000 @@ -163,7 +163,7 @@ void load(char *); struct var * lookup(char *); int mail(struct name *, struct name *, struct name *, struct name *, - char *, char *); + char *, char *, char *, char *); void mail1(struct header *, int); void makemessage(FILE *, int); void mark(int); Index: mail.1 =================================================================== RCS file: /cvs/src/usr.bin/mail/mail.1,v retrieving revision 1.83 diff -u -p -r1.83 mail.1 --- mail.1 31 Mar 2022 17:27:25 -0000 1.83 +++ mail.1 19 Sep 2023 13:30:15 -0000 @@ -45,6 +45,8 @@ .Op Fl c Ar list .Op Fl r Ar from-addr .Op Fl s Ar subject +.Op Fl e Ar transfer-encoding +.Op Fl y Ar content-type .Ar to-addr ... .Ek .Nm mail @@ -77,6 +79,8 @@ Causes .Nm mail to output all sorts of information useful for debugging .Nm mail . +.It Fl e Ar encoding +Add a Content-Transfer-Enconding header on command line. .It Fl E Don't send messages with an empty body. .It Fl f @@ -133,6 +137,8 @@ except that locking is done. Verbose mode. The details of delivery are displayed on the user's terminal. +.It Fl y Ar content-type +Add a Content-Type header on command line. .El .Ss Startup actions At startup time, Index: main.c =================================================================== RCS file: /cvs/src/usr.bin/mail/main.c,v retrieving revision 1.35 diff -u -p -r1.35 main.c --- main.c 26 Jan 2021 18:21:47 -0000 1.35 +++ main.c 19 Sep 2023 13:30:15 -0000 @@ -103,6 +103,8 @@ main(int argc, char **argv) struct name *to, *cc, *bcc, *smopts; char *fromaddr; char *subject; + char *encoding; + char *type; char *ef; char nosrc = 0; char *rc; @@ -136,7 +138,9 @@ main(int argc, char **argv) smopts = NULL; fromaddr = NULL; subject = NULL; - while ((i = getopt(argc, argv, "EINb:c:dfinr:s:u:v")) != -1) { + encoding = NULL; + type = NULL; + while ((i = getopt(argc, argv, "EINb:c:de:finr:s:u:vy:")) != -1) { switch (i) { case 'u': /* @@ -158,6 +162,10 @@ main(int argc, char **argv) case 'd': debug++; break; + case 'e': + /* Set Content-Transfer-Enconding */ + encoding = optarg; + break; case 'r': /* * Set From: address @@ -198,6 +206,10 @@ main(int argc, char **argv) */ assign("verbose", ""); break; + case 'y': + /* Set Content-Type */ + type = optarg; + break; case 'I': /* * We're interactive @@ -241,8 +253,8 @@ main(int argc, char **argv) /* * Check for inconsistent arguments. */ - if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL || - fromaddr != NULL)) + if (to == NULL && (subject != NULL || encoding != NULL || + type != NULL || cc != NULL || bcc != NULL || fromaddr != NULL)) errx(1, "You must specify direct recipients with -s, -c, -b, " "or -r"); /* @@ -269,7 +281,7 @@ main(int argc, char **argv) rc = "~/.mailrc"; load(expand(rc)); if (!rcvmode) { - mail(to, cc, bcc, smopts, fromaddr, subject); + mail(to, cc, bcc, smopts, fromaddr, subject, encoding, type); /* * why wait? */ @@ -338,7 +350,8 @@ usage(void) { fprintf(stderr, "usage: %s [-dEIinv] [-b list] [-c list] " - "[-r from-addr] [-s subject] to-addr ...\n", __progname); + "[-r from-addr] [-s subject] [-e encoding] [-y content-type ] " + "to-addr ...\n", __progname); fprintf(stderr, " %s [-dEIiNnv] -f [file]\n", __progname); fprintf(stderr, " %s [-dEIiNnv] [-u user]\n", __progname); exit(1); Index: names.c =================================================================== RCS file: /cvs/src/usr.bin/mail/names.c,v retrieving revision 1.25 diff -u -p -r1.25 names.c --- names.c 28 Jun 2019 13:35:02 -0000 1.25 +++ names.c 19 Sep 2023 13:30:15 -0000 @@ -252,7 +252,7 @@ outof(struct name *names, FILE *fo, stru goto cant; } fprintf(fout, "From %s %s", myname, date); - puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); + puthead(hp, fout, GTO|GSUBJECT|GENCODING|GTYPE|GCC|GNL); while ((c = getc(fo)) != EOF) (void)putc(c, fout); rewind(fo); Index: send.c =================================================================== RCS file: /cvs/src/usr.bin/mail/send.c,v retrieving revision 1.26 diff -u -p -r1.26 send.c --- send.c 8 Mar 2023 04:43:11 -0000 1.26 +++ send.c 19 Sep 2023 13:30:15 -0000 @@ -279,13 +279,15 @@ statusput(struct message *mp, FILE *obuf */ int mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, - char *fromaddr, char *subject) + char *fromaddr, char *subject, char *encoding, char *type) { struct header head; head.h_to = to; head.h_from = fromaddr; head.h_subject = subject; + head.h_encoding = encoding; + head.h_type = type; head.h_cc = cc; head.h_bcc = bcc; head.h_smopts = smopts; @@ -306,6 +308,8 @@ sendmail(void *v) head.h_to = extract(str, GTO); head.h_from = NULL; head.h_subject = NULL; + head.h_encoding = NULL; + head.h_type = NULL; head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; @@ -482,7 +486,7 @@ infix(struct header *hp, FILE *fi) return(fi); } (void)rm(tempname); - (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA); + (void)puthead(hp, nfo, GTO|GSUBJECT|GENCODING|GTYPE|GCC|GBCC|GNL|GCOMMA); c = getc(fi); while (c != EOF) { (void)putc(c, nfo); @@ -508,7 +512,7 @@ infix(struct header *hp, FILE *fi) } /* - * Dump the to, subject, cc header on the + * Dump the to, subject, encoding, content-type, cc header on the * passed file buffer. */ int @@ -525,6 +529,12 @@ puthead(struct header *hp, FILE *fo, int fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; if (hp->h_subject != NULL && w & GSUBJECT) fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; + if (hp->h_encoding != NULL && w & GENCODING) + fprintf(fo, "Content-Transfer-Encoding: %s\n", + hp->h_encoding), gotcha++; + if (hp->h_type != NULL && w & GTYPE) + fprintf(fo, "Content-Type: %s\n", + hp->h_type), gotcha++; if (hp->h_cc != NULL && w & GCC) fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; if (hp->h_bcc != NULL && w & GBCC) Index: tty.c =================================================================== RCS file: /cvs/src/usr.bin/mail/tty.c,v retrieving revision 1.23 diff -u -p -r1.23 tty.c --- tty.c 8 Mar 2023 04:43:11 -0000 1.23 +++ tty.c 19 Sep 2023 13:30:15 -0000 @@ -132,6 +132,18 @@ grabh(struct header *hp, int gflags) goto out; hp->h_subject = s; } + if (gflags & GENCODING) { + s = readtty("Content-Transfer-Encoding: ", hp->h_encoding); + if (s == NULL) + goto out; + hp->h_encoding = s; + } + if (gflags & GTYPE) { + s = readtty("Content-Type: ", hp->h_type); + if (s == NULL) + goto out; + hp->h_type = s; + } if (gflags & GCC) { s = readtty("Cc: ", detract(hp->h_cc, 0)); if (s == NULL) -- Walter