]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
wall: use fputs_careful()
authorнаб <nabijaczleweli@nabijaczleweli.xyz>
Wed, 15 Mar 2023 15:16:48 +0000 (16:16 +0100)
committerKarel Zak <kzak@redhat.com>
Fri, 17 Mar 2023 10:48:41 +0000 (11:48 +0100)
LINE_MAX only applies to teletypes in canonical mode: when stdin is a
file, it could still very much tear; start off at 512 for the sprintf(),
then use getline() like in write.

The line wrapping has one suboptimal edge-case:
  $ wall < all

  Broadcast message from nabijaczleweli@tarta (pts/4) (Tue Mar 14 22:31:25
  2023):

  ^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_
  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
  KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?\200\201\202\203\204\205\206
  \207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232
  \233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256
  \257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302
  \303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326
  \327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352
  \353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376
  \377
but that's a pathological input, and the result is still infinitely
better than it was before, so fixing that is more trouble than it's
worth.

Bug-Debian: https://bugs.debian.org/826596

include/carefulputc.h
login-utils/last.c
term-utils/wall.c
term-utils/write.c

index 740add68e8281bc4f5023b13f8ab1ec4dc2349d6..3cc6f7ff9972a11b77e50f7e914d6f3985a4157c 100644 (file)
@@ -6,6 +6,7 @@
 #include <ctype.h>
 #ifdef HAVE_WIDECHAR
 #include <wctype.h>
+#include <wchar.h>
 #endif
 #include <stdbool.h>
 
  * 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.
+ * but it's a solid bet that it's compatible with the author's.
+ * Use soft_width=0 to disable wrapping.
  */
-static inline int fputs_careful(const char * s, FILE *fp, const char ctrl, bool cr_lf)
+static inline int fputs_careful(const char * s, FILE *fp, const char ctrl, bool cr_lf, int soft_width)
 {
-       int ret = 0;
+       int ret = 0, col = 0;
 
        for (size_t slen = strlen(s); *s; ++s, --slen) {
-               if (*s == '\n')
+               if (*s == '\t')
+                       col += (7 - (col % 8)) - 1;
+               else if (*s == '\r')
+                       col = -1;
+               else if (*s == '\a')
+                       --col;
+
+               if ((soft_width && col >= soft_width) || *s == '\n') {
+                       if (soft_width) {
+                               fprintf(fp, "%*s", soft_width - col, "");
+                               col = 0;
+                       }
                        ret = fputs(cr_lf ? "\r\n" : "\n", fp);
-               else if (isprint(*s) || *s == '\a' || *s == '\t' || *s == '\r')
+                       if (*s == '\n' || ret < 0)
+                               goto wrote;
+               }
+
+               if (isprint(*s) || *s == '\a' || *s == '\t' || *s == '\r') {
                        ret = putc(*s, fp);
-               else if (!c_isascii(*s)) {
+                       ++col;
+               } else if (!c_isascii(*s)) {
 #ifdef HAVE_WIDECHAR
                        wchar_t w;
                        size_t clen = mbtowc(&w, s, slen);
@@ -35,21 +53,27 @@ static inline int fputs_careful(const char * s, FILE *fp, const char ctrl, bool
                                case (size_t)-1:  // EILSEQ
                                        mbtowc(NULL, NULL, 0);
                                nonprint:
-                                       ret = fprintf(fp, "\\%3hho", *s);
+                                       col += ret = fprintf(fp, "\\%3hho", *s);
                                        break;
                                default:
                                        if(!iswprint(w))
                                                goto nonprint;
                                        ret = fwrite(s, 1, clen, fp);
+                                       if (soft_width)
+                                               col += wcwidth(w);
                                        s += clen - 1;
                                        slen -= clen - 1;
                                        break;
                        }
 #else
-                       ret = fprintf(fp, "\\%3hho", *s);
+                       col += ret = fprintf(fp, "\\%3hho", *s);
 #endif
-               } else
+               } else {
                        ret = fputs((char[]){ ctrl, *s ^ 0x40, '\0' }, fp);
+                       col += 2;
+               }
+
+       wrote:
                if (ret < 0)
                        return EOF;
        }
index 1b45dbf24d5b685f591b8d298a24a8eb4a3e1ddb..37c6abe972cb58e520442f8c886a5311235d9e28 100644 (file)
@@ -547,7 +547,7 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
        /*
         *      Print out "final" string safely.
         */
-       fputs_careful(final, stdout, '*', false);
+       fputs_careful(final, stdout, '*', false, 0);
 
        if (len < 0 || (size_t)len >= sizeof(final))
                putchar('\n');
index a51a9282935ee61d740e14448a1fc4c2f8d3e1e4..377db451837174951b9bec016497d60fbb0a32ee 100644 (file)
@@ -274,29 +274,13 @@ int main(int argc, char **argv)
        exit(EXIT_SUCCESS);
 }
 
-static void buf_putc_careful(FILE *fs, int c)
-{
-       if (isprint(c) || c == '\a' || c == '\t' || c == '\r' || c == '\n')
-               fputc(c, fs);
-       else if (!c_isascii(c))
-               fprintf(fs, "\\%3o", (unsigned char)c);
-       else
-               fputs((char[]){ '^', c ^ 0x40, '\0' }, fs);
-}
-
 static char *makemsg(char *fname, char **mvec, int mvecsz,
                     size_t *mbufsize, int print_banner)
 {
-       register int ch, cnt;
-       char *p, *lbuf, *retbuf;
+       char *lbuf, *retbuf;
        FILE * fs = open_memstream(&retbuf, mbufsize);
-       long line_max;
-
-       line_max = sysconf(_SC_LINE_MAX);
-       if (line_max <= 0)
-               line_max = 512;
-
-       lbuf = xmalloc(line_max);
+       size_t lbuflen = 512;
+       lbuf = xmalloc(lbuflen);
 
        if (print_banner == TRUE) {
                char *hostname = xgethostname();
@@ -329,7 +313,7 @@ static char *makemsg(char *fname, char **mvec, int mvecsz,
                   will not overflow as long as %d takes at most 100 chars */
                fprintf(fs, "\r%*s\r\n", TERM_WIDTH, " ");
 
-               snprintf(lbuf, line_max,
+               snprintf(lbuf, lbuflen,
                                _("Broadcast message from %s@%s (%s) (%s):"),
                                whom, hostname, where, date);
                fprintf(fs, "%-*.*s\007\007\r\n", TERM_WIDTH, TERM_WIDTH, lbuf);
@@ -373,18 +357,8 @@ static char *makemsg(char *fname, char **mvec, int mvecsz,
                /*
                 * Read message from stdin.
                 */
-               while (fgets(lbuf, line_max, stdin)) {
-                       for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) {
-                               if (cnt == TERM_WIDTH || ch == '\n') {
-                                       fprintf(fs, "%*s\r\n", TERM_WIDTH - cnt, "");
-                                       cnt = 0;
-                               }
-                               if (ch == '\t')
-                                       cnt += (7 - (cnt % 8));
-                               if (ch != '\n')
-                                       buf_putc_careful(fs, ch);
-                       }
-               }
+               while (getline(&lbuf, &lbuflen, stdin) >= 0)
+                       fputs_careful(lbuf, fs, '^', true, TERM_WIDTH);
        }
        fprintf(fs, "%*s\r\n", TERM_WIDTH, " ");
 
index b485e28fd5e432f9490a8d6c1910dc4385420449..a5a21280c3c22abbdea61f89c91907474fbcb6a4 100644 (file)
@@ -276,7 +276,7 @@ static void do_write(const struct write_control *ctl)
                if (signal_received)
                        break;
 
-               if (fputs_careful(line, stdout, '^', true) == EOF)
+               if (fputs_careful(line, stdout, '^', true, 0) == EOF)
                        err(EXIT_FAILURE, _("carefulputc failed"));
        }
        free(line);