diff options
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | progressmeter.c | 51 | ||||
-rw-r--r-- | scp.c | 45 | ||||
-rw-r--r-- | sftp-client.c | 9 | ||||
-rw-r--r-- | sftp.c | 46 | ||||
-rw-r--r-- | utf8.c | 258 | ||||
-rw-r--r-- | utf8.h | 24 |
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} \ compat.o crc32.o deattack.o fatal.o hostfile.o \ log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ - atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ + atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o utf.o \ monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ 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 @@ -/* $OpenBSD: progressmeter.c,v 1.42 2016/03/02 22:42:40 dtucker Exp $ */ +/* $OpenBSD: progressmeter.c,v 1.43 2016/05/25 23:48:45 schwarze Exp $ */ /* * Copyright (c) 2003 Nils Nordman. All rights reserved. * @@ -31,6 +31,7 @@ #include <errno.h> #include <signal.h> +#include <stdarg.h> #include <stdio.h> #include <string.h> #include <time.h> @@ -39,6 +40,7 @@ #include "progressmeter.h" #include "atomicio.h" #include "misc.h" +#include "utf8.h" #define DEFAULT_WINSIZE 80 #define MAX_WINSIZE 512 @@ -119,14 +121,14 @@ format_size(char *buf, int size, off_t bytes) void refresh_progress_meter(void) { - char buf[MAX_WINSIZE + 1]; + char buf[MAX_WINSIZE * 4 + 1]; off_t transferred; double elapsed, now; int percent; off_t bytes_left; int cur_speed; int hours, minutes, seconds; - int i, len; + size_t i; int file_len; transferred = *counter - (cur_pos ? cur_pos : start_pos); @@ -157,17 +159,16 @@ refresh_progress_meter(void) bytes_per_second = cur_speed; /* filename */ - buf[0] = '\0'; + buf[0] = '\r'; + buf[1] = '\0'; file_len = win_size - 35; if (file_len > 0) { - len = snprintf(buf, file_len + 1, "\r%s", file); - if (len < 0) - len = 0; - if (len >= file_len + 1) - len = file_len; - for (i = len; i < file_len; i++) - buf[i] = ' '; - buf[file_len] = '\0'; + (void) snmprintf(buf + 1, sizeof(buf) - 1 - 35, + &file_len, "%s", file); + i = strlen(buf); + while (++file_len < win_size - 35 && i + 1 < sizeof(buf)) + buf[i++] = ' '; + buf[i] = '\0'; } /* percent of transfer done */ @@ -175,18 +176,18 @@ refresh_progress_meter(void) percent = ((float)cur_pos / end_pos) * 100; else percent = 100; - snprintf(buf + strlen(buf), win_size - strlen(buf), + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %3d%% ", percent); /* amount transferred */ - format_size(buf + strlen(buf), win_size - strlen(buf), + format_size(buf + strlen(buf), sizeof(buf) - strlen(buf), cur_pos); - strlcat(buf, " ", win_size); + strlcat(buf, " ", sizeof(buf)); /* bandwidth usage */ - format_rate(buf + strlen(buf), win_size - strlen(buf), + format_rate(buf + strlen(buf), sizeof(buf) - strlen(buf), (off_t)bytes_per_second); - strlcat(buf, "/s ", win_size); + strlcat(buf, "/s ", sizeof(buf)); /* ETA */ if (!transferred) @@ -195,9 +196,9 @@ refresh_progress_meter(void) stalled = 0; if (stalled >= STALL_TIME) - strlcat(buf, "- stalled -", win_size); + strlcat(buf, "- stalled -", sizeof(buf)); else if (bytes_per_second == 0 && bytes_left) - strlcat(buf, " --:-- ETA", win_size); + strlcat(buf, " --:-- ETA", sizeof(buf)); else { if (bytes_left > 0) seconds = bytes_left / bytes_per_second; @@ -210,19 +211,21 @@ refresh_progress_meter(void) seconds -= minutes * 60; if (hours != 0) - snprintf(buf + strlen(buf), win_size - strlen(buf), + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%d:%02d:%02d", hours, minutes, seconds); else - snprintf(buf + strlen(buf), win_size - strlen(buf), + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %02d:%02d", minutes, seconds); if (bytes_left > 0) - strlcat(buf, " ETA", win_size); + strlcat(buf, " ETA", sizeof(buf)); else - strlcat(buf, " ", win_size); + strlcat(buf, " ", sizeof(buf)); } + if (win_size < 35) + buf[win_size] = '\0'; - atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1); + atomicio(vwrite, STDOUT_FILENO, buf, strlen(buf)); last_update = now; } @@ -1,4 +1,4 @@ -/* $OpenBSD: scp.c,v 1.185 2016/03/02 22:43:52 dtucker Exp $ */ +/* $OpenBSD: scp.c,v 1.186 2016/05/25 23:48:45 schwarze Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). @@ -96,6 +96,7 @@ #include <errno.h> #include <fcntl.h> #include <limits.h> +#include <locale.h> #include <pwd.h> #include <signal.h> #include <stdarg.h> @@ -114,6 +115,7 @@ #include "log.h" #include "misc.h" #include "progressmeter.h" +#include "utf8.h" extern char *__progname; @@ -191,7 +193,7 @@ do_local_cmd(arglist *a) if (verbose_mode) { fprintf(stderr, "Executing:"); for (i = 0; i < a->num; i++) - fprintf(stderr, " %s", a->list[i]); + fmprintf(stderr, " %s", a->list[i]); fprintf(stderr, "\n"); } if ((pid = fork()) == -1) @@ -232,7 +234,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) int pin[2], pout[2], reserved[2]; if (verbose_mode) - fprintf(stderr, + fmprintf(stderr, "Executing: program %s host %s, user %s, command %s\n", ssh_program, host, remuser ? remuser : "(unspecified)", cmd); @@ -307,7 +309,7 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) int status; if (verbose_mode) - fprintf(stderr, + fmprintf(stderr, "Executing: 2nd program %s host %s, user %s, command %s\n", ssh_program, host, remuser ? remuser : "(unspecified)", cmd); @@ -378,6 +380,8 @@ main(int argc, char **argv) /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); + setlocale(LC_CTYPE, ""); + /* Copy argv, because we modify it */ newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); for (n = 0; n < argc; n++) @@ -810,9 +814,8 @@ syserr: run_err("%s: %s", name, strerror(errno)); snprintf(buf, sizeof buf, "C%04o %lld %s\n", (u_int) (stb.st_mode & FILEMODEMASK), (long long)stb.st_size, last); - if (verbose_mode) { - fprintf(stderr, "Sending file modes: %s", buf); - } + if (verbose_mode) + fmprintf(stderr, "Sending file modes: %s", buf); (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; @@ -889,7 +892,7 @@ rsource(char *name, struct stat *statp) (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) - fprintf(stderr, "Entering directory: %s", path); + fmprintf(stderr, "Entering directory: %s", path); (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); @@ -929,7 +932,7 @@ sink(int argc, char **argv) off_t size, statbytes; unsigned long long ull; int setimes, targisdir, wrerrno = 0; - char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; struct timeval tv[2]; #define atime tv[0] @@ -964,12 +967,15 @@ sink(int argc, char **argv) } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (verbose_mode) - fprintf(stderr, "Sink: %s", buf); + fmprintf(stderr, "Sink: %s", buf); if (buf[0] == '\01' || buf[0] == '\02') { - if (iamremote == 0) + if (iamremote == 0) { + (void) snmprintf(visbuf, sizeof(visbuf), + NULL, "%s", buf + 1); (void) atomicio(vwrite, STDERR_FILENO, - buf + 1, strlen(buf + 1)); + visbuf, strlen(visbuf)); + } if (buf[0] == '\02') exit(1); ++errs; @@ -1212,7 +1218,7 @@ screwup: int response(void) { - char ch, *cp, resp, rbuf[2048]; + char ch, *cp, resp, rbuf[2048], visbuf[2048]; if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); @@ -1232,8 +1238,13 @@ response(void) *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); - if (!iamremote) - (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); + if (!iamremote) { + cp[-1] = '\0'; + (void) snmprintf(visbuf, sizeof(visbuf), + NULL, "%s\n", rbuf); + (void) atomicio(vwrite, STDERR_FILENO, + visbuf, strlen(visbuf)); + } ++errs; if (resp == 1) return (-1); @@ -1271,7 +1282,7 @@ run_err(const char *fmt,...) if (!iamremote) { va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + vfmprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } @@ -1317,7 +1328,7 @@ okname(char *cp0) } while (*++cp); return (1); -bad: fprintf(stderr, "%s: invalid user name\n", cp0); +bad: fmprintf(stderr, "%s: invalid user name\n", cp0); return (0); } 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 @@ -/* $OpenBSD: sftp-client.c,v 1.123 2016/05/02 08:49:03 djm Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.124 2016/05/25 23:48:45 schwarze Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * @@ -53,6 +53,7 @@ #include "atomicio.h" #include "progressmeter.h" #include "misc.h" +#include "utf8.h" #include "sftp.h" #include "sftp-common.h" @@ -610,7 +611,7 @@ do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, } if (print_flag) - printf("%s\n", longname); + mprintf("%s\n", longname); /* * Directory entries should never contain '/' @@ -1460,7 +1461,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, return -1; } if (print_flag) - printf("Retrieving %s\n", src); + mprintf("Retrieving %s\n", src); if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) mode = dirattrib->perm & 01777; @@ -1793,7 +1794,7 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, return -1; } if (print_flag) - printf("Entering %s\n", src); + mprintf("Entering %s\n", src); attrib_clear(&a); stat_to_attrib(&sb, &a); @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.173 2016/04/08 08:19:17 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.174 2016/05/25 23:48:45 schwarze Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * @@ -49,6 +49,7 @@ typedef void EditLine; #endif #include <limits.h> #include <signal.h> +#include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -63,6 +64,7 @@ typedef void EditLine; #include "log.h" #include "pathnames.h" #include "misc.h" +#include "utf8.h" #include "sftp.h" #include "ssherr.h" @@ -644,9 +646,11 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, resume |= global_aflag; if (!quiet && resume) - printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst); + mprintf("Resuming %s to %s\n", + g.gl_pathv[i], abs_dst); else if (!quiet && !resume) - printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + mprintf("Fetching %s to %s\n", + g.gl_pathv[i], abs_dst); if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, pflag || global_pflag, 1, resume, @@ -735,10 +739,11 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, resume |= global_aflag; if (!quiet && resume) - printf("Resuming upload of %s to %s\n", g.gl_pathv[i], - abs_dst); + mprintf("Resuming upload of %s to %s\n", + g.gl_pathv[i], abs_dst); else if (!quiet && !resume) - printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); + mprintf("Uploading %s to %s\n", + g.gl_pathv[i], abs_dst); if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (upload_dir(conn, g.gl_pathv[i], abs_dst, pflag || global_pflag, 1, resume, @@ -839,12 +844,12 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) attrib_to_stat(&d[n]->a, &sb); lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); - printf("%s\n", lname); + mprintf("%s\n", lname); free(lname); } else - printf("%s\n", d[n]->longname); + mprintf("%s\n", d[n]->longname); } else { - printf("%-*s", colspace, fname); + mprintf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; @@ -925,10 +930,10 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, } lname = ls_file(fname, g.gl_statv[i], 1, (lflag & LS_SI_UNITS)); - printf("%s\n", lname); + mprintf("%s\n", lname); free(lname); } else { - printf("%-*s", colspace, fname); + mprintf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; @@ -1456,7 +1461,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!quiet) - printf("Removing %s\n", g.gl_pathv[i]); + mprintf("Removing %s\n", g.gl_pathv[i]); err = do_rm(conn, g.gl_pathv[i]); if (err != 0 && err_abort) break; @@ -1556,7 +1561,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!quiet) - printf("Changing mode on %s\n", g.gl_pathv[i]); + mprintf("Changing mode on %s\n", + g.gl_pathv[i]); err = do_setstat(conn, g.gl_pathv[i], &a); if (err != 0 && err_abort) break; @@ -1586,12 +1592,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; if (cmdnum == I_CHOWN) { if (!quiet) - printf("Changing owner on %s\n", + mprintf("Changing owner on %s\n", g.gl_pathv[i]); aa->uid = n_arg; } else { if (!quiet) - printf("Changing group on %s\n", + mprintf("Changing group on %s\n", g.gl_pathv[i]); aa->gid = n_arg; } @@ -1601,7 +1607,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, } break; case I_PWD: - printf("Remote working directory: %s\n", *pwd); + mprintf("Remote working directory: %s\n", *pwd); break; case I_LPWD: if (!getcwd(path_buf, sizeof(path_buf))) { @@ -1609,7 +1615,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, err = -1; break; } - printf("Local working directory: %s\n", path_buf); + mprintf("Local working directory: %s\n", path_buf); break; case I_QUIT: /* Processed below */ @@ -1678,7 +1684,7 @@ complete_display(char **list, u_int len) for (y = 0; list[y]; y++) { llen = strlen(list[y]); tmp = llen > len ? list[y] + len : ""; - printf("%-*s", colspace, tmp); + mprintf("%-*s", colspace, tmp); if (m >= columns) { printf("\n"); m = 1; @@ -2062,7 +2068,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) if (remote_is_dir(conn, dir) && file2 == NULL) { if (!quiet) - printf("Changing to: %s\n", dir); + mprintf("Changing to: %s\n", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); if (parse_dispatch_command(conn, cmd, &remote_path, 1) != 0) { @@ -2106,7 +2112,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) break; } if (!interactive) { /* Echo command */ - printf("sftp> %s", cmd); + mprintf("sftp> %s", cmd); if (strlen(cmd) > 0 && cmd[strlen(cmd) - 1] != '\n') printf("\n"); @@ -0,0 +1,258 @@ +/* $OpenBSD: utf8.c,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */ +/* + * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Utility functions for multibyte-character handling, + * in particular to sanitize untrusted strings for terminal output. + */ + +#include <sys/types.h> +#include <langinfo.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vis.h> +#include <wchar.h> + +#include "utf8.h" + +static int dangerous_locale(void); +static int vasnmprintf(char **, size_t, int *, const char *, va_list); + + +/* + * For US-ASCII and UTF-8 encodings, we can safely recover from + * encoding errors and from non-printable characters. For any + * other encodings, err to the side of caution and abort parsing: + * For state-dependent encodings, recovery is impossible. + * For arbitrary encodings, replacement of non-printable + * characters would be non-trivial and too fragile. + */ + +static int +dangerous_locale(void) { + char *loc; + + loc = nl_langinfo(CODESET); + return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8"); +} + +/* + * The following two functions limit the number of bytes written, + * including the terminating '\0', to sz. Unless wp is NULL, + * they limit the number of display columns occupied to *wp. + * Whichever is reached first terminates the output string. + * To stay close to the standard interfaces, they return the number of + * non-NUL bytes that would have been written if both were unlimited. + * If wp is NULL, newline, carriage return, and tab are allowed; + * otherwise, the actual number of columns occupied by what was + * written is returned in *wp. + */ + +static int +vasnmprintf(char **str, size_t maxsz, int *wp, const char *fmt, va_list ap) +{ + char *src; /* Source string returned from vasprintf. */ + char *sp; /* Pointer into src. */ + char *dst; /* Destination string to be returned. */ + char *dp; /* Pointer into dst. */ + char *tp; /* Temporary pointer for dst. */ + size_t sz; /* Number of bytes allocated for dst. */ + size_t tsz; /* Temporary size while extending dst. */ + wchar_t wc; /* Wide character at sp. */ + int len; /* Number of bytes in the character at sp. */ + int ret; /* Number of bytes needed to format src. */ + int width; /* Display width of the character wc. */ + int total_width, max_width, print; + + src = dst = NULL; + if (vasprintf(&src, fmt, ap) <= 0) + goto fail; + + sz = strlen(src); + if ((dst = malloc(sz)) == NULL) + goto fail; + + if (maxsz > INT_MAX) + maxsz = INT_MAX; + + sp = src; + dp = dst; + ret = 0; + print = 1; + total_width = 0; + max_width = wp == NULL ? INT_MAX : *wp; + while (*sp != '\0') { + if ((len = mbtowc(&wc, sp, MB_CUR_MAX)) == -1) { + (void)mbtowc(NULL, NULL, MB_CUR_MAX); + if (dangerous_locale()) { + ret = -1; + break; + } + len = 1; + width = -1; + } else if (wp == NULL && + (wc == L'\n' || wc == L'\r' || wc == L'\t')) { + /* + * Don't use width uninitialized; the actual + * value doesn't matter because total_width + * is only returned for wp != NULL. + */ + width = 0; + } else if ((width = wcwidth(wc)) == -1 && + dangerous_locale()) { + ret = -1; + break; + } + + /* Valid, printable character. */ + + if (width >= 0) { + if (print && (dp - dst >= (int)maxsz - len || + total_width > max_width - width)) + print = 0; + if (print) { + total_width += width; + memcpy(dp, sp, len); + dp += len; + } + sp += len; + if (ret >= 0) + ret += len; + continue; + } + + /* Escaping required. */ + + while (len > 0) { + if (print && (dp - dst >= (int)maxsz - 4 || + total_width > max_width - 4)) + print = 0; + if (print) { + if (dp + 4 >= dst + sz) { + tsz = sz + 128; + if (tsz > maxsz) + tsz = maxsz; + tp = realloc(dst, tsz); + if (tp == NULL) { + ret = -1; + break; + } + dp = tp + (dp - dst); + dst = tp; + sz = tsz; + } + tp = vis(dp, *sp, VIS_OCTAL | VIS_ALL, 0); + width = tp - dp; + total_width += width; + dp = tp; + } else + width = 4; + len--; + sp++; + if (ret >= 0) + ret += width; + } + if (len > 0) + break; + } + free(src); + *dp = '\0'; + *str = dst; + if (wp != NULL) + *wp = total_width; + + /* + * If the string was truncated by the width limit but + * would have fit into the size limit, the only sane way + * to report the problem is using the return value, such + * that the usual idiom "if (ret < 0 || ret >= sz) error" + * works as expected. + */ + + if (ret < (int)maxsz && !print) + ret = -1; + return ret; + +fail: + free(src); + free(dst); + *str = NULL; + if (wp != NULL) + *wp = 0; + return -1; +} + +int +snmprintf(char *str, size_t sz, int *wp, const char *fmt, ...) +{ + va_list ap; + char *cp; + int ret; + + va_start(ap, fmt); + ret = vasnmprintf(&cp, sz, wp, fmt, ap); + va_end(ap); + (void)strlcpy(str, cp, sz); + free(cp); + return ret; +} + +/* + * To stay close to the standard interfaces, the following functions + * return the number of non-NUL bytes written. + */ + +int +vfmprintf(FILE *stream, const char *fmt, va_list ap) +{ + char *str; + int ret; + + if ((ret = vasnmprintf(&str, INT_MAX, NULL, fmt, ap)) < 0) + return -1; + if (fputs(str, stream) == EOF) + ret = -1; + free(str); + return ret; +} + +int +fmprintf(FILE *stream, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfmprintf(stream, fmt, ap); + va_end(ap); + return ret; +} + +int +mprintf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfmprintf(stdout, fmt, ap); + va_end(ap); + return ret; +} @@ -0,0 +1,24 @@ +/* $OpenBSD: utf8.h,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */ +/* + * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int mprintf(const char *, ...) + __attribute__((format(printf, 1, 2))); +int fmprintf(FILE *, const char *, ...) + __attribute__((format(printf, 2, 3))); +int vfmprintf(FILE *, const char *, va_list); +int snmprintf(char *, size_t, int *, const char *, ...) + __attribute__((format(printf, 4, 5))); |