summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in2
-rw-r--r--progressmeter.c51
-rw-r--r--scp.c45
-rw-r--r--sftp-client.c9
-rw-r--r--sftp.c46
-rw-r--r--utf8.c258
-rw-r--r--utf8.h24
7 files changed, 369 insertions, 66 deletions
diff --git a/Makefile.in b/Makefile.in
index af758d035..6267fefda 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -82,7 +82,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
82 compat.o crc32.o deattack.o fatal.o hostfile.o \ 82 compat.o crc32.o deattack.o fatal.o hostfile.o \
83 log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ 83 log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \
84 readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ 84 readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
85 atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ 85 atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o utf.o \
86 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ 86 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
87 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ 87 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
88 ssh-pkcs11.o smult_curve25519_ref.o \ 88 ssh-pkcs11.o smult_curve25519_ref.o \
diff --git a/progressmeter.c b/progressmeter.c
index 3a455408c..4fed2f4f0 100644
--- a/progressmeter.c
+++ b/progressmeter.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: progressmeter.c,v 1.42 2016/03/02 22:42:40 dtucker Exp $ */ 1/* $OpenBSD: progressmeter.c,v 1.43 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * Copyright (c) 2003 Nils Nordman. All rights reserved. 3 * Copyright (c) 2003 Nils Nordman. All rights reserved.
4 * 4 *
@@ -31,6 +31,7 @@
31 31
32#include <errno.h> 32#include <errno.h>
33#include <signal.h> 33#include <signal.h>
34#include <stdarg.h>
34#include <stdio.h> 35#include <stdio.h>
35#include <string.h> 36#include <string.h>
36#include <time.h> 37#include <time.h>
@@ -39,6 +40,7 @@
39#include "progressmeter.h" 40#include "progressmeter.h"
40#include "atomicio.h" 41#include "atomicio.h"
41#include "misc.h" 42#include "misc.h"
43#include "utf8.h"
42 44
43#define DEFAULT_WINSIZE 80 45#define DEFAULT_WINSIZE 80
44#define MAX_WINSIZE 512 46#define MAX_WINSIZE 512
@@ -119,14 +121,14 @@ format_size(char *buf, int size, off_t bytes)
119void 121void
120refresh_progress_meter(void) 122refresh_progress_meter(void)
121{ 123{
122 char buf[MAX_WINSIZE + 1]; 124 char buf[MAX_WINSIZE * 4 + 1];
123 off_t transferred; 125 off_t transferred;
124 double elapsed, now; 126 double elapsed, now;
125 int percent; 127 int percent;
126 off_t bytes_left; 128 off_t bytes_left;
127 int cur_speed; 129 int cur_speed;
128 int hours, minutes, seconds; 130 int hours, minutes, seconds;
129 int i, len; 131 size_t i;
130 int file_len; 132 int file_len;
131 133
132 transferred = *counter - (cur_pos ? cur_pos : start_pos); 134 transferred = *counter - (cur_pos ? cur_pos : start_pos);
@@ -157,17 +159,16 @@ refresh_progress_meter(void)
157 bytes_per_second = cur_speed; 159 bytes_per_second = cur_speed;
158 160
159 /* filename */ 161 /* filename */
160 buf[0] = '\0'; 162 buf[0] = '\r';
163 buf[1] = '\0';
161 file_len = win_size - 35; 164 file_len = win_size - 35;
162 if (file_len > 0) { 165 if (file_len > 0) {
163 len = snprintf(buf, file_len + 1, "\r%s", file); 166 (void) snmprintf(buf + 1, sizeof(buf) - 1 - 35,
164 if (len < 0) 167 &file_len, "%s", file);
165 len = 0; 168 i = strlen(buf);
166 if (len >= file_len + 1) 169 while (++file_len < win_size - 35 && i + 1 < sizeof(buf))
167 len = file_len; 170 buf[i++] = ' ';
168 for (i = len; i < file_len; i++) 171 buf[i] = '\0';
169 buf[i] = ' ';
170 buf[file_len] = '\0';
171 } 172 }
172 173
173 /* percent of transfer done */ 174 /* percent of transfer done */
@@ -175,18 +176,18 @@ refresh_progress_meter(void)
175 percent = ((float)cur_pos / end_pos) * 100; 176 percent = ((float)cur_pos / end_pos) * 100;
176 else 177 else
177 percent = 100; 178 percent = 100;
178 snprintf(buf + strlen(buf), win_size - strlen(buf), 179 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
179 " %3d%% ", percent); 180 " %3d%% ", percent);
180 181
181 /* amount transferred */ 182 /* amount transferred */
182 format_size(buf + strlen(buf), win_size - strlen(buf), 183 format_size(buf + strlen(buf), sizeof(buf) - strlen(buf),
183 cur_pos); 184 cur_pos);
184 strlcat(buf, " ", win_size); 185 strlcat(buf, " ", sizeof(buf));
185 186
186 /* bandwidth usage */ 187 /* bandwidth usage */
187 format_rate(buf + strlen(buf), win_size - strlen(buf), 188 format_rate(buf + strlen(buf), sizeof(buf) - strlen(buf),
188 (off_t)bytes_per_second); 189 (off_t)bytes_per_second);
189 strlcat(buf, "/s ", win_size); 190 strlcat(buf, "/s ", sizeof(buf));
190 191
191 /* ETA */ 192 /* ETA */
192 if (!transferred) 193 if (!transferred)
@@ -195,9 +196,9 @@ refresh_progress_meter(void)
195 stalled = 0; 196 stalled = 0;
196 197
197 if (stalled >= STALL_TIME) 198 if (stalled >= STALL_TIME)
198 strlcat(buf, "- stalled -", win_size); 199 strlcat(buf, "- stalled -", sizeof(buf));
199 else if (bytes_per_second == 0 && bytes_left) 200 else if (bytes_per_second == 0 && bytes_left)
200 strlcat(buf, " --:-- ETA", win_size); 201 strlcat(buf, " --:-- ETA", sizeof(buf));
201 else { 202 else {
202 if (bytes_left > 0) 203 if (bytes_left > 0)
203 seconds = bytes_left / bytes_per_second; 204 seconds = bytes_left / bytes_per_second;
@@ -210,19 +211,21 @@ refresh_progress_meter(void)
210 seconds -= minutes * 60; 211 seconds -= minutes * 60;
211 212
212 if (hours != 0) 213 if (hours != 0)
213 snprintf(buf + strlen(buf), win_size - strlen(buf), 214 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
214 "%d:%02d:%02d", hours, minutes, seconds); 215 "%d:%02d:%02d", hours, minutes, seconds);
215 else 216 else
216 snprintf(buf + strlen(buf), win_size - strlen(buf), 217 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
217 " %02d:%02d", minutes, seconds); 218 " %02d:%02d", minutes, seconds);
218 219
219 if (bytes_left > 0) 220 if (bytes_left > 0)
220 strlcat(buf, " ETA", win_size); 221 strlcat(buf, " ETA", sizeof(buf));
221 else 222 else
222 strlcat(buf, " ", win_size); 223 strlcat(buf, " ", sizeof(buf));
223 } 224 }
225 if (win_size < 35)
226 buf[win_size] = '\0';
224 227
225 atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1); 228 atomicio(vwrite, STDOUT_FILENO, buf, strlen(buf));
226 last_update = now; 229 last_update = now;
227} 230}
228 231
diff --git a/scp.c b/scp.c
index 3f0d75090..43ca3fa09 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: scp.c,v 1.185 2016/03/02 22:43:52 dtucker Exp $ */ 1/* $OpenBSD: scp.c,v 1.186 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * scp - secure remote copy. This is basically patched BSD rcp which 3 * scp - secure remote copy. This is basically patched BSD rcp which
4 * uses ssh to do the data transfer (instead of using rcmd). 4 * uses ssh to do the data transfer (instead of using rcmd).
@@ -96,6 +96,7 @@
96#include <errno.h> 96#include <errno.h>
97#include <fcntl.h> 97#include <fcntl.h>
98#include <limits.h> 98#include <limits.h>
99#include <locale.h>
99#include <pwd.h> 100#include <pwd.h>
100#include <signal.h> 101#include <signal.h>
101#include <stdarg.h> 102#include <stdarg.h>
@@ -114,6 +115,7 @@
114#include "log.h" 115#include "log.h"
115#include "misc.h" 116#include "misc.h"
116#include "progressmeter.h" 117#include "progressmeter.h"
118#include "utf8.h"
117 119
118extern char *__progname; 120extern char *__progname;
119 121
@@ -191,7 +193,7 @@ do_local_cmd(arglist *a)
191 if (verbose_mode) { 193 if (verbose_mode) {
192 fprintf(stderr, "Executing:"); 194 fprintf(stderr, "Executing:");
193 for (i = 0; i < a->num; i++) 195 for (i = 0; i < a->num; i++)
194 fprintf(stderr, " %s", a->list[i]); 196 fmprintf(stderr, " %s", a->list[i]);
195 fprintf(stderr, "\n"); 197 fprintf(stderr, "\n");
196 } 198 }
197 if ((pid = fork()) == -1) 199 if ((pid = fork()) == -1)
@@ -232,7 +234,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
232 int pin[2], pout[2], reserved[2]; 234 int pin[2], pout[2], reserved[2];
233 235
234 if (verbose_mode) 236 if (verbose_mode)
235 fprintf(stderr, 237 fmprintf(stderr,
236 "Executing: program %s host %s, user %s, command %s\n", 238 "Executing: program %s host %s, user %s, command %s\n",
237 ssh_program, host, 239 ssh_program, host,
238 remuser ? remuser : "(unspecified)", cmd); 240 remuser ? remuser : "(unspecified)", cmd);
@@ -307,7 +309,7 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
307 int status; 309 int status;
308 310
309 if (verbose_mode) 311 if (verbose_mode)
310 fprintf(stderr, 312 fmprintf(stderr,
311 "Executing: 2nd program %s host %s, user %s, command %s\n", 313 "Executing: 2nd program %s host %s, user %s, command %s\n",
312 ssh_program, host, 314 ssh_program, host,
313 remuser ? remuser : "(unspecified)", cmd); 315 remuser ? remuser : "(unspecified)", cmd);
@@ -378,6 +380,8 @@ main(int argc, char **argv)
378 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 380 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
379 sanitise_stdfd(); 381 sanitise_stdfd();
380 382
383 setlocale(LC_CTYPE, "");
384
381 /* Copy argv, because we modify it */ 385 /* Copy argv, because we modify it */
382 newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); 386 newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv));
383 for (n = 0; n < argc; n++) 387 for (n = 0; n < argc; n++)
@@ -810,9 +814,8 @@ syserr: run_err("%s: %s", name, strerror(errno));
810 snprintf(buf, sizeof buf, "C%04o %lld %s\n", 814 snprintf(buf, sizeof buf, "C%04o %lld %s\n",
811 (u_int) (stb.st_mode & FILEMODEMASK), 815 (u_int) (stb.st_mode & FILEMODEMASK),
812 (long long)stb.st_size, last); 816 (long long)stb.st_size, last);
813 if (verbose_mode) { 817 if (verbose_mode)
814 fprintf(stderr, "Sending file modes: %s", buf); 818 fmprintf(stderr, "Sending file modes: %s", buf);
815 }
816 (void) atomicio(vwrite, remout, buf, strlen(buf)); 819 (void) atomicio(vwrite, remout, buf, strlen(buf));
817 if (response() < 0) 820 if (response() < 0)
818 goto next; 821 goto next;
@@ -889,7 +892,7 @@ rsource(char *name, struct stat *statp)
889 (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 892 (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n",
890 (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 893 (u_int) (statp->st_mode & FILEMODEMASK), 0, last);
891 if (verbose_mode) 894 if (verbose_mode)
892 fprintf(stderr, "Entering directory: %s", path); 895 fmprintf(stderr, "Entering directory: %s", path);
893 (void) atomicio(vwrite, remout, path, strlen(path)); 896 (void) atomicio(vwrite, remout, path, strlen(path));
894 if (response() < 0) { 897 if (response() < 0) {
895 closedir(dirp); 898 closedir(dirp);
@@ -929,7 +932,7 @@ sink(int argc, char **argv)
929 off_t size, statbytes; 932 off_t size, statbytes;
930 unsigned long long ull; 933 unsigned long long ull;
931 int setimes, targisdir, wrerrno = 0; 934 int setimes, targisdir, wrerrno = 0;
932 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 935 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
933 struct timeval tv[2]; 936 struct timeval tv[2];
934 937
935#define atime tv[0] 938#define atime tv[0]
@@ -964,12 +967,15 @@ sink(int argc, char **argv)
964 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 967 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
965 *cp = 0; 968 *cp = 0;
966 if (verbose_mode) 969 if (verbose_mode)
967 fprintf(stderr, "Sink: %s", buf); 970 fmprintf(stderr, "Sink: %s", buf);
968 971
969 if (buf[0] == '\01' || buf[0] == '\02') { 972 if (buf[0] == '\01' || buf[0] == '\02') {
970 if (iamremote == 0) 973 if (iamremote == 0) {
974 (void) snmprintf(visbuf, sizeof(visbuf),
975 NULL, "%s", buf + 1);
971 (void) atomicio(vwrite, STDERR_FILENO, 976 (void) atomicio(vwrite, STDERR_FILENO,
972 buf + 1, strlen(buf + 1)); 977 visbuf, strlen(visbuf));
978 }
973 if (buf[0] == '\02') 979 if (buf[0] == '\02')
974 exit(1); 980 exit(1);
975 ++errs; 981 ++errs;
@@ -1212,7 +1218,7 @@ screwup:
1212int 1218int
1213response(void) 1219response(void)
1214{ 1220{
1215 char ch, *cp, resp, rbuf[2048]; 1221 char ch, *cp, resp, rbuf[2048], visbuf[2048];
1216 1222
1217 if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 1223 if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp))
1218 lostconn(0); 1224 lostconn(0);
@@ -1232,8 +1238,13 @@ response(void)
1232 *cp++ = ch; 1238 *cp++ = ch;
1233 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 1239 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
1234 1240
1235 if (!iamremote) 1241 if (!iamremote) {
1236 (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 1242 cp[-1] = '\0';
1243 (void) snmprintf(visbuf, sizeof(visbuf),
1244 NULL, "%s\n", rbuf);
1245 (void) atomicio(vwrite, STDERR_FILENO,
1246 visbuf, strlen(visbuf));
1247 }
1237 ++errs; 1248 ++errs;
1238 if (resp == 1) 1249 if (resp == 1)
1239 return (-1); 1250 return (-1);
@@ -1271,7 +1282,7 @@ run_err(const char *fmt,...)
1271 1282
1272 if (!iamremote) { 1283 if (!iamremote) {
1273 va_start(ap, fmt); 1284 va_start(ap, fmt);
1274 vfprintf(stderr, fmt, ap); 1285 vfmprintf(stderr, fmt, ap);
1275 va_end(ap); 1286 va_end(ap);
1276 fprintf(stderr, "\n"); 1287 fprintf(stderr, "\n");
1277 } 1288 }
@@ -1317,7 +1328,7 @@ okname(char *cp0)
1317 } while (*++cp); 1328 } while (*++cp);
1318 return (1); 1329 return (1);
1319 1330
1320bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1331bad: fmprintf(stderr, "%s: invalid user name\n", cp0);
1321 return (0); 1332 return (0);
1322} 1333}
1323 1334
diff --git a/sftp-client.c b/sftp-client.c
index faf14684c..0ca44a4d6 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.123 2016/05/02 08:49:03 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.124 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -53,6 +53,7 @@
53#include "atomicio.h" 53#include "atomicio.h"
54#include "progressmeter.h" 54#include "progressmeter.h"
55#include "misc.h" 55#include "misc.h"
56#include "utf8.h"
56 57
57#include "sftp.h" 58#include "sftp.h"
58#include "sftp-common.h" 59#include "sftp-common.h"
@@ -610,7 +611,7 @@ do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
610 } 611 }
611 612
612 if (print_flag) 613 if (print_flag)
613 printf("%s\n", longname); 614 mprintf("%s\n", longname);
614 615
615 /* 616 /*
616 * Directory entries should never contain '/' 617 * Directory entries should never contain '/'
@@ -1460,7 +1461,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1460 return -1; 1461 return -1;
1461 } 1462 }
1462 if (print_flag) 1463 if (print_flag)
1463 printf("Retrieving %s\n", src); 1464 mprintf("Retrieving %s\n", src);
1464 1465
1465 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 1466 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1466 mode = dirattrib->perm & 01777; 1467 mode = dirattrib->perm & 01777;
@@ -1793,7 +1794,7 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1793 return -1; 1794 return -1;
1794 } 1795 }
1795 if (print_flag) 1796 if (print_flag)
1796 printf("Entering %s\n", src); 1797 mprintf("Entering %s\n", src);
1797 1798
1798 attrib_clear(&a); 1799 attrib_clear(&a);
1799 stat_to_attrib(&sb, &a); 1800 stat_to_attrib(&sb, &a);
diff --git a/sftp.c b/sftp.c
index 3d2d13aaf..6a7048431 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.173 2016/04/08 08:19:17 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.174 2016/05/25 23:48:45 schwarze Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -49,6 +49,7 @@ typedef void EditLine;
49#endif 49#endif
50#include <limits.h> 50#include <limits.h>
51#include <signal.h> 51#include <signal.h>
52#include <stdarg.h>
52#include <stdlib.h> 53#include <stdlib.h>
53#include <stdio.h> 54#include <stdio.h>
54#include <string.h> 55#include <string.h>
@@ -63,6 +64,7 @@ typedef void EditLine;
63#include "log.h" 64#include "log.h"
64#include "pathnames.h" 65#include "pathnames.h"
65#include "misc.h" 66#include "misc.h"
67#include "utf8.h"
66 68
67#include "sftp.h" 69#include "sftp.h"
68#include "ssherr.h" 70#include "ssherr.h"
@@ -644,9 +646,11 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
644 646
645 resume |= global_aflag; 647 resume |= global_aflag;
646 if (!quiet && resume) 648 if (!quiet && resume)
647 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst); 649 mprintf("Resuming %s to %s\n",
650 g.gl_pathv[i], abs_dst);
648 else if (!quiet && !resume) 651 else if (!quiet && !resume)
649 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 652 mprintf("Fetching %s to %s\n",
653 g.gl_pathv[i], abs_dst);
650 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 654 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
651 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 655 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
652 pflag || global_pflag, 1, resume, 656 pflag || global_pflag, 1, resume,
@@ -735,10 +739,11 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
735 739
736 resume |= global_aflag; 740 resume |= global_aflag;
737 if (!quiet && resume) 741 if (!quiet && resume)
738 printf("Resuming upload of %s to %s\n", g.gl_pathv[i], 742 mprintf("Resuming upload of %s to %s\n",
739 abs_dst); 743 g.gl_pathv[i], abs_dst);
740 else if (!quiet && !resume) 744 else if (!quiet && !resume)
741 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 745 mprintf("Uploading %s to %s\n",
746 g.gl_pathv[i], abs_dst);
742 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 747 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
743 if (upload_dir(conn, g.gl_pathv[i], abs_dst, 748 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
744 pflag || global_pflag, 1, resume, 749 pflag || global_pflag, 1, resume,
@@ -839,12 +844,12 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
839 attrib_to_stat(&d[n]->a, &sb); 844 attrib_to_stat(&d[n]->a, &sb);
840 lname = ls_file(fname, &sb, 1, 845 lname = ls_file(fname, &sb, 1,
841 (lflag & LS_SI_UNITS)); 846 (lflag & LS_SI_UNITS));
842 printf("%s\n", lname); 847 mprintf("%s\n", lname);
843 free(lname); 848 free(lname);
844 } else 849 } else
845 printf("%s\n", d[n]->longname); 850 mprintf("%s\n", d[n]->longname);
846 } else { 851 } else {
847 printf("%-*s", colspace, fname); 852 mprintf("%-*s", colspace, fname);
848 if (c >= columns) { 853 if (c >= columns) {
849 printf("\n"); 854 printf("\n");
850 c = 1; 855 c = 1;
@@ -925,10 +930,10 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
925 } 930 }
926 lname = ls_file(fname, g.gl_statv[i], 1, 931 lname = ls_file(fname, g.gl_statv[i], 1,
927 (lflag & LS_SI_UNITS)); 932 (lflag & LS_SI_UNITS));
928 printf("%s\n", lname); 933 mprintf("%s\n", lname);
929 free(lname); 934 free(lname);
930 } else { 935 } else {
931 printf("%-*s", colspace, fname); 936 mprintf("%-*s", colspace, fname);
932 if (c >= columns) { 937 if (c >= columns) {
933 printf("\n"); 938 printf("\n");
934 c = 1; 939 c = 1;
@@ -1456,7 +1461,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1456 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1461 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1457 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1462 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1458 if (!quiet) 1463 if (!quiet)
1459 printf("Removing %s\n", g.gl_pathv[i]); 1464 mprintf("Removing %s\n", g.gl_pathv[i]);
1460 err = do_rm(conn, g.gl_pathv[i]); 1465 err = do_rm(conn, g.gl_pathv[i]);
1461 if (err != 0 && err_abort) 1466 if (err != 0 && err_abort)
1462 break; 1467 break;
@@ -1556,7 +1561,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1556 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1561 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1557 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1562 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1558 if (!quiet) 1563 if (!quiet)
1559 printf("Changing mode on %s\n", g.gl_pathv[i]); 1564 mprintf("Changing mode on %s\n",
1565 g.gl_pathv[i]);
1560 err = do_setstat(conn, g.gl_pathv[i], &a); 1566 err = do_setstat(conn, g.gl_pathv[i], &a);
1561 if (err != 0 && err_abort) 1567 if (err != 0 && err_abort)
1562 break; 1568 break;
@@ -1586,12 +1592,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1586 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1592 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1587 if (cmdnum == I_CHOWN) { 1593 if (cmdnum == I_CHOWN) {
1588 if (!quiet) 1594 if (!quiet)
1589 printf("Changing owner on %s\n", 1595 mprintf("Changing owner on %s\n",
1590 g.gl_pathv[i]); 1596 g.gl_pathv[i]);
1591 aa->uid = n_arg; 1597 aa->uid = n_arg;
1592 } else { 1598 } else {
1593 if (!quiet) 1599 if (!quiet)
1594 printf("Changing group on %s\n", 1600 mprintf("Changing group on %s\n",
1595 g.gl_pathv[i]); 1601 g.gl_pathv[i]);
1596 aa->gid = n_arg; 1602 aa->gid = n_arg;
1597 } 1603 }
@@ -1601,7 +1607,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1601 } 1607 }
1602 break; 1608 break;
1603 case I_PWD: 1609 case I_PWD:
1604 printf("Remote working directory: %s\n", *pwd); 1610 mprintf("Remote working directory: %s\n", *pwd);
1605 break; 1611 break;
1606 case I_LPWD: 1612 case I_LPWD:
1607 if (!getcwd(path_buf, sizeof(path_buf))) { 1613 if (!getcwd(path_buf, sizeof(path_buf))) {
@@ -1609,7 +1615,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1609 err = -1; 1615 err = -1;
1610 break; 1616 break;
1611 } 1617 }
1612 printf("Local working directory: %s\n", path_buf); 1618 mprintf("Local working directory: %s\n", path_buf);
1613 break; 1619 break;
1614 case I_QUIT: 1620 case I_QUIT:
1615 /* Processed below */ 1621 /* Processed below */
@@ -1678,7 +1684,7 @@ complete_display(char **list, u_int len)
1678 for (y = 0; list[y]; y++) { 1684 for (y = 0; list[y]; y++) {
1679 llen = strlen(list[y]); 1685 llen = strlen(list[y]);
1680 tmp = llen > len ? list[y] + len : ""; 1686 tmp = llen > len ? list[y] + len : "";
1681 printf("%-*s", colspace, tmp); 1687 mprintf("%-*s", colspace, tmp);
1682 if (m >= columns) { 1688 if (m >= columns) {
1683 printf("\n"); 1689 printf("\n");
1684 m = 1; 1690 m = 1;
@@ -2062,7 +2068,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2062 2068
2063 if (remote_is_dir(conn, dir) && file2 == NULL) { 2069 if (remote_is_dir(conn, dir) && file2 == NULL) {
2064 if (!quiet) 2070 if (!quiet)
2065 printf("Changing to: %s\n", dir); 2071 mprintf("Changing to: %s\n", dir);
2066 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 2072 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2067 if (parse_dispatch_command(conn, cmd, 2073 if (parse_dispatch_command(conn, cmd,
2068 &remote_path, 1) != 0) { 2074 &remote_path, 1) != 0) {
@@ -2106,7 +2112,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2106 break; 2112 break;
2107 } 2113 }
2108 if (!interactive) { /* Echo command */ 2114 if (!interactive) { /* Echo command */
2109 printf("sftp> %s", cmd); 2115 mprintf("sftp> %s", cmd);
2110 if (strlen(cmd) > 0 && 2116 if (strlen(cmd) > 0 &&
2111 cmd[strlen(cmd) - 1] != '\n') 2117 cmd[strlen(cmd) - 1] != '\n')
2112 printf("\n"); 2118 printf("\n");
diff --git a/utf8.c b/utf8.c
new file mode 100644
index 000000000..d6089bdec
--- /dev/null
+++ b/utf8.c
@@ -0,0 +1,258 @@
1/* $OpenBSD: utf8.c,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*
19 * Utility functions for multibyte-character handling,
20 * in particular to sanitize untrusted strings for terminal output.
21 */
22
23#include <sys/types.h>
24#include <langinfo.h>
25#include <limits.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <vis.h>
31#include <wchar.h>
32
33#include "utf8.h"
34
35static int dangerous_locale(void);
36static int vasnmprintf(char **, size_t, int *, const char *, va_list);
37
38
39/*
40 * For US-ASCII and UTF-8 encodings, we can safely recover from
41 * encoding errors and from non-printable characters. For any
42 * other encodings, err to the side of caution and abort parsing:
43 * For state-dependent encodings, recovery is impossible.
44 * For arbitrary encodings, replacement of non-printable
45 * characters would be non-trivial and too fragile.
46 */
47
48static int
49dangerous_locale(void) {
50 char *loc;
51
52 loc = nl_langinfo(CODESET);
53 return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8");
54}
55
56/*
57 * The following two functions limit the number of bytes written,
58 * including the terminating '\0', to sz. Unless wp is NULL,
59 * they limit the number of display columns occupied to *wp.
60 * Whichever is reached first terminates the output string.
61 * To stay close to the standard interfaces, they return the number of
62 * non-NUL bytes that would have been written if both were unlimited.
63 * If wp is NULL, newline, carriage return, and tab are allowed;
64 * otherwise, the actual number of columns occupied by what was
65 * written is returned in *wp.
66 */
67
68static int
69vasnmprintf(char **str, size_t maxsz, int *wp, const char *fmt, va_list ap)
70{
71 char *src; /* Source string returned from vasprintf. */
72 char *sp; /* Pointer into src. */
73 char *dst; /* Destination string to be returned. */
74 char *dp; /* Pointer into dst. */
75 char *tp; /* Temporary pointer for dst. */
76 size_t sz; /* Number of bytes allocated for dst. */
77 size_t tsz; /* Temporary size while extending dst. */
78 wchar_t wc; /* Wide character at sp. */
79 int len; /* Number of bytes in the character at sp. */
80 int ret; /* Number of bytes needed to format src. */
81 int width; /* Display width of the character wc. */
82 int total_width, max_width, print;
83
84 src = dst = NULL;
85 if (vasprintf(&src, fmt, ap) <= 0)
86 goto fail;
87
88 sz = strlen(src);
89 if ((dst = malloc(sz)) == NULL)
90 goto fail;
91
92 if (maxsz > INT_MAX)
93 maxsz = INT_MAX;
94
95 sp = src;
96 dp = dst;
97 ret = 0;
98 print = 1;
99 total_width = 0;
100 max_width = wp == NULL ? INT_MAX : *wp;
101 while (*sp != '\0') {
102 if ((len = mbtowc(&wc, sp, MB_CUR_MAX)) == -1) {
103 (void)mbtowc(NULL, NULL, MB_CUR_MAX);
104 if (dangerous_locale()) {
105 ret = -1;
106 break;
107 }
108 len = 1;
109 width = -1;
110 } else if (wp == NULL &&
111 (wc == L'\n' || wc == L'\r' || wc == L'\t')) {
112 /*
113 * Don't use width uninitialized; the actual
114 * value doesn't matter because total_width
115 * is only returned for wp != NULL.
116 */
117 width = 0;
118 } else if ((width = wcwidth(wc)) == -1 &&
119 dangerous_locale()) {
120 ret = -1;
121 break;
122 }
123
124 /* Valid, printable character. */
125
126 if (width >= 0) {
127 if (print && (dp - dst >= (int)maxsz - len ||
128 total_width > max_width - width))
129 print = 0;
130 if (print) {
131 total_width += width;
132 memcpy(dp, sp, len);
133 dp += len;
134 }
135 sp += len;
136 if (ret >= 0)
137 ret += len;
138 continue;
139 }
140
141 /* Escaping required. */
142
143 while (len > 0) {
144 if (print && (dp - dst >= (int)maxsz - 4 ||
145 total_width > max_width - 4))
146 print = 0;
147 if (print) {
148 if (dp + 4 >= dst + sz) {
149 tsz = sz + 128;
150 if (tsz > maxsz)
151 tsz = maxsz;
152 tp = realloc(dst, tsz);
153 if (tp == NULL) {
154 ret = -1;
155 break;
156 }
157 dp = tp + (dp - dst);
158 dst = tp;
159 sz = tsz;
160 }
161 tp = vis(dp, *sp, VIS_OCTAL | VIS_ALL, 0);
162 width = tp - dp;
163 total_width += width;
164 dp = tp;
165 } else
166 width = 4;
167 len--;
168 sp++;
169 if (ret >= 0)
170 ret += width;
171 }
172 if (len > 0)
173 break;
174 }
175 free(src);
176 *dp = '\0';
177 *str = dst;
178 if (wp != NULL)
179 *wp = total_width;
180
181 /*
182 * If the string was truncated by the width limit but
183 * would have fit into the size limit, the only sane way
184 * to report the problem is using the return value, such
185 * that the usual idiom "if (ret < 0 || ret >= sz) error"
186 * works as expected.
187 */
188
189 if (ret < (int)maxsz && !print)
190 ret = -1;
191 return ret;
192
193fail:
194 free(src);
195 free(dst);
196 *str = NULL;
197 if (wp != NULL)
198 *wp = 0;
199 return -1;
200}
201
202int
203snmprintf(char *str, size_t sz, int *wp, const char *fmt, ...)
204{
205 va_list ap;
206 char *cp;
207 int ret;
208
209 va_start(ap, fmt);
210 ret = vasnmprintf(&cp, sz, wp, fmt, ap);
211 va_end(ap);
212 (void)strlcpy(str, cp, sz);
213 free(cp);
214 return ret;
215}
216
217/*
218 * To stay close to the standard interfaces, the following functions
219 * return the number of non-NUL bytes written.
220 */
221
222int
223vfmprintf(FILE *stream, const char *fmt, va_list ap)
224{
225 char *str;
226 int ret;
227
228 if ((ret = vasnmprintf(&str, INT_MAX, NULL, fmt, ap)) < 0)
229 return -1;
230 if (fputs(str, stream) == EOF)
231 ret = -1;
232 free(str);
233 return ret;
234}
235
236int
237fmprintf(FILE *stream, const char *fmt, ...)
238{
239 va_list ap;
240 int ret;
241
242 va_start(ap, fmt);
243 ret = vfmprintf(stream, fmt, ap);
244 va_end(ap);
245 return ret;
246}
247
248int
249mprintf(const char *fmt, ...)
250{
251 va_list ap;
252 int ret;
253
254 va_start(ap, fmt);
255 ret = vfmprintf(stdout, fmt, ap);
256 va_end(ap);
257 return ret;
258}
diff --git a/utf8.h b/utf8.h
new file mode 100644
index 000000000..43ce1d55d
--- /dev/null
+++ b/utf8.h
@@ -0,0 +1,24 @@
1/* $OpenBSD: utf8.h,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18int mprintf(const char *, ...)
19 __attribute__((format(printf, 1, 2)));
20int fmprintf(FILE *, const char *, ...)
21 __attribute__((format(printf, 2, 3)));
22int vfmprintf(FILE *, const char *, va_list);
23int snmprintf(char *, size_t, int *, const char *, ...)
24 __attribute__((format(printf, 4, 5)));