summaryrefslogtreecommitdiffstats
path: root/debian/patches/write-correctly-handle-wide-characters.patch
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/patches/write-correctly-handle-wide-characters.patch192
1 files changed, 192 insertions, 0 deletions
diff --git a/debian/patches/write-correctly-handle-wide-characters.patch b/debian/patches/write-correctly-handle-wide-characters.patch
new file mode 100644
index 0000000..57954ac
--- /dev/null
+++ b/debian/patches/write-correctly-handle-wide-characters.patch
@@ -0,0 +1,192 @@
+From: наб <nabijaczleweli@nabijaczleweli.xyz>
+Date: Wed, 15 Mar 2023 16:16:31 +0100
+Subject: write: correctly handle wide characters
+
+Do this by replacing fputc_careful() (notice that the description said
+it's locale-aware ‒ it very much is /not/), with a fputs_careful() which
+does the same thing, but if it were to output a byte in the \123 format,
+first it checks whether this byte starts a valid multibyte character.
+
+If it does, and that character is printable, write it verbatim.
+This means that
+ echo 'foo åäö ąęćźżń bar' | write nabijaczleweli pts/4
+instead of
+ foo \303\245\303\244\303\266
+ \304\205\304\231\304\207\305\272\305\274\305\204 bar
+yields
+ foo åäö ąęćźżń bar
+or, more realistically, from a message I got earlier today,
+ Filip powiedzia\305\202 \305\274e zap\305\202aci jutro
+becomes
+ Filip powiedział że zapłaci jutro
+
+Invalid/non-printable sequences get processed as before.
+
+Line reading in write must become getline() to avoid dealing with
+partial characters: for example on input consisting solely of
+ąęćźżń, where every {1} is an instance, the output would be
+ {42}ąęć\305\272żń{84}ąęćź\305\274ń{84}ąęćźż\305\204{39}
+with just fixed-512 fgets()
+
+Origin: https://github.com/util-linux/util-linux/commit/8a7b8456d1dc0e7ca557d1ac31f638986704757f
+Bug-Debian: https://bugs.debian.org/826596
+---
+ include/carefulputc.h | 62 +++++++++++++++++++++++++++++++++++++--------------
+ login-utils/last.c | 4 +---
+ term-utils/write.c | 25 ++++++---------------
+ 3 files changed, 53 insertions(+), 38 deletions(-)
+
+diff --git a/include/carefulputc.h b/include/carefulputc.h
+index 54a02bf..38a61cc 100644
+--- a/include/carefulputc.h
++++ b/include/carefulputc.h
+@@ -1,29 +1,57 @@
+ #ifndef UTIL_LINUX_CAREFULPUTC_H
+ #define UTIL_LINUX_CAREFULPUTC_H
+
+-/*
+- * A putc() for use in write and wall (that sometimes are sgid tty).
+- * It avoids control characters in our locale, and also ASCII control
+- * characters. Note that the locale of the recipient is unknown.
+-*/
+ #include <stdio.h>
+ #include <string.h>
+ #include <ctype.h>
++#ifdef HAVE_WIDECHAR
++#include <wctype.h>
++#endif
++#include <stdbool.h>
+
+-static inline int fputc_careful(int c, FILE *fp, const char fail)
++/*
++ * A puts() for use in write and wall (that sometimes are sgid tty).
++ * It avoids control and invalid characters.
++ * The locale of the recipient is nominally unknown,
++ * but it's a solid bet that the encoding is compatible with the author's.
++ */
++static inline int fputs_careful(const char * s, FILE *fp, const char ctrl, bool cr_lf)
+ {
+- int ret;
+-
+- if (isprint(c) || c == '\a' || c == '\t' || c == '\r' || c == '\n')
+- ret = putc(c, fp);
+- else if (!isascii(c))
+- ret = fprintf(fp, "\\%3o", (unsigned char)c);
+- else {
+- ret = putc(fail, fp);
+- if (ret != EOF)
+- ret = putc(c ^ 0x40, fp);
++ int ret = 0;
++
++ for (size_t slen = strlen(s); *s; ++s, --slen) {
++ if (*s == '\n')
++ ret = fputs(cr_lf ? "\r\n" : "\n", fp);
++ else if (isprint(*s) || *s == '\a' || *s == '\t' || *s == '\r')
++ ret = putc(*s, fp);
++ else if (!isascii(*s)) {
++#ifdef HAVE_WIDECHAR
++ wchar_t w;
++ size_t clen = mbtowc(&w, s, slen);
++ switch(clen) {
++ case (size_t)-2: // incomplete
++ case (size_t)-1: // EILSEQ
++ mbtowc(NULL, NULL, 0);
++ nonprint:
++ ret = fprintf(fp, "\\%3hho", *s);
++ break;
++ default:
++ if(!iswprint(w))
++ goto nonprint;
++ ret = fwrite(s, 1, clen, fp);
++ s += clen - 1;
++ slen -= clen - 1;
++ break;
++ }
++#else
++ ret = fprintf(fp, "\\%3hho", *s);
++#endif
++ } else
++ ret = fputs((char[]){ ctrl, *s ^ 0x40, '\0' }, fp);
++ if (ret < 0)
++ return EOF;
+ }
+- return (ret < 0) ? EOF : 0;
++ return 0;
+ }
+
+ /*
+diff --git a/login-utils/last.c b/login-utils/last.c
+index 6b25efd..e6a8c41 100644
+--- a/login-utils/last.c
++++ b/login-utils/last.c
+@@ -386,7 +386,6 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
+ char final[512];
+ char utline[sizeof(p->ut_line) + 1];
+ char domain[256];
+- char *s;
+ int mins, hours, days;
+ int r, len;
+ struct last_timefmt *fmt;
+@@ -543,8 +542,7 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
+ /*
+ * Print out "final" string safely.
+ */
+- for (s = final; *s; s++)
+- fputc_careful(*s, stdout, '*');
++ fputs_careful(final, stdout, '*', false);
+
+ if (len < 0 || (size_t)len >= sizeof(final))
+ putchar('\n');
+diff --git a/term-utils/write.c b/term-utils/write.c
+index bd64fc2..231c77f 100644
+--- a/term-utils/write.c
++++ b/term-utils/write.c
+@@ -223,21 +223,6 @@ static void signal_handler(int signo)
+ signal_received = signo;
+ }
+
+-/*
+- * write_line - like fputs(), but makes control characters visible and
+- * turns \n into \r\n.
+- */
+-static void write_line(char *s)
+-{
+- while (*s) {
+- const int c = *s++;
+-
+- if ((c == '\n' && fputc_careful('\r', stdout, '^') == EOF)
+- || fputc_careful(c, stdout, '^') == EOF)
+- err(EXIT_FAILURE, _("carefulputc failed"));
+- }
+-}
+-
+ /*
+ * do_write - actually make the connection
+ */
+@@ -247,7 +232,8 @@ static void do_write(const struct write_control *ctl)
+ struct passwd *pwd;
+ time_t now;
+ struct tm *tm;
+- char *host, line[512];
++ char *host, *line = NULL;
++ size_t linelen = 0;
+ struct sigaction sigact;
+
+ /* Determine our login name(s) before the we reopen() stdout */
+@@ -286,11 +272,14 @@ static void do_write(const struct write_control *ctl)
+ free(host);
+ printf("\r\n");
+
+- while (fgets(line, sizeof(line), stdin) != NULL) {
++ while (getline(&line, &linelen, stdin) >= 0) {
+ if (signal_received)
+ break;
+- write_line(line);
++
++ if (fputs_careful(line, stdout, '^', true) == EOF)
++ err(EXIT_FAILURE, _("carefulputc failed"));
+ }
++ free(line);
+ printf("EOF\r\n");
+ }
+