1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
From: =?utf-8?b?0L3QsNCx?= <nabijaczleweli@nabijaczleweli.xyz>
Date: Wed, 15 Mar 2023 16:16:48 +0100
Subject: wall: use fputs_careful()
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 | 42 +++++++++++++++++++++++++++++++++---------
login-utils/last.c | 2 +-
term-utils/wall.c | 38 ++++++--------------------------------
term-utils/write.c | 2 +-
4 files changed, 41 insertions(+), 43 deletions(-)
diff --git a/include/carefulputc.h b/include/carefulputc.h
index 4f74526..51d1c99 100644
--- a/include/carefulputc.h
+++ b/include/carefulputc.h
@@ -6,6 +6,7 @@
#include <ctype.h>
#ifdef HAVE_WIDECHAR
#include <wctype.h>
+#include <wchar.h>
#endif
#include <stdbool.h>
@@ -15,18 +16,35 @@
* 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;
}
diff --git a/login-utils/last.c b/login-utils/last.c
index c5984f1..8386f08 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -550,7 +550,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');
diff --git a/term-utils/wall.c b/term-utils/wall.c
index 97623b8..fa5e027 100644
--- a/term-utils/wall.c
+++ b/term-utils/wall.c
@@ -273,29 +273,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();
@@ -330,7 +314,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);
@@ -374,18 +358,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, " ");
diff --git a/term-utils/write.c b/term-utils/write.c
index 710a58c..1d57fce 100644
--- a/term-utils/write.c
+++ b/term-utils/write.c
@@ -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);
|