diff --git a/contrib/win32/openssh/libssh.vcxproj b/contrib/win32/openssh/libssh.vcxproj
index 475be4e..f1f1ad7 100644
--- a/contrib/win32/openssh/libssh.vcxproj
+++ b/contrib/win32/openssh/libssh.vcxproj
@@ -266,6 +266,7 @@
true
+
diff --git a/contrib/win32/openssh/libssh.vcxproj.filters b/contrib/win32/openssh/libssh.vcxproj.filters
index fa793bc..156819e 100644
--- a/contrib/win32/openssh/libssh.vcxproj.filters
+++ b/contrib/win32/openssh/libssh.vcxproj.filters
@@ -294,6 +294,9 @@
Source Files
+
+ Source Files
+
diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c
index 8f0022f..ec14c93 100644
--- a/contrib/win32/win32compat/w32fd.c
+++ b/contrib/win32/win32compat/w32fd.c
@@ -854,12 +854,10 @@ int
w32_ftruncate(int fd, off_t length) {
CHECK_FD(fd);
+ if (!SetFilePointer(w32_fd_to_handle(fd), length, 0, FILE_BEGIN))
+ return -1;
if (!SetEndOfFile(w32_fd_to_handle(fd)))
return -1;
- if (!SetFileValidData(w32_fd_to_handle(fd), length))
- return -1;
- if (!SetFilePointer(w32_fd_to_handle(fd), 0, 0, FILE_BEGIN))
- return -1;
return 0;
}
\ No newline at end of file
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h
index 0823028..1e69891 100644
--- a/openbsd-compat/openbsd-compat.h
+++ b/openbsd-compat/openbsd-compat.h
@@ -232,10 +232,45 @@ long long strtonum(const char *, long long, long long, const char **);
# define mblen(x, y) (1)
#endif
+#ifndef HAVE_WCWIDTH
+# define wcwidth(x) (((x) >= 0x20 && (x) <= 0x7e) ? 1 : -1)
+/* force our no-op nl_langinfo and mbtowc */
+# undef HAVE_NL_LANGINFO
+# undef HAVE_MBTOWC
+# undef HAVE_LANGINFO_H
+#endif
+
+#ifndef HAVE_NL_LANGINFO
+# define nl_langinfo(x) ""
+#endif
+
+#ifndef HAVE_MBTOWC
+int mbtowc(wchar_t *, const char*, size_t);
+#endif
+
+
#if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF)
# include
#endif
+/*
+* Some platforms unconditionally undefine va_copy() so we define VA_COPY()
+* instead. This is known to be the case on at least some configurations of
+* AIX with the xlc compiler.
+*/
+#ifndef VA_COPY
+# ifdef HAVE_VA_COPY
+# define VA_COPY(dest, src) va_copy(dest, src)
+# else
+# ifdef HAVE___VA_COPY
+# define VA_COPY(dest, src) __va_copy(dest, src)
+# else
+# define VA_COPY(dest, src) (dest) = (src)
+# endif
+# endif
+#endif
+
+
#ifndef HAVE_VASPRINTF
int vasprintf(char **, const char *, va_list);
#endif
diff --git a/openbsd-compat/vis.h b/openbsd-compat/vis.h
index d1286c9..b6ee6ec 100644
--- a/openbsd-compat/vis.h
+++ b/openbsd-compat/vis.h
@@ -48,7 +48,7 @@
*/
#define VIS_OCTAL 0x01 /* use octal \ddd format */
#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */
-
+#define VIS_ALL 0x400 /* encode all characters */
/*
* to alter set of characters encoded (default is to encode all
* non-graphic except space, tab, and newline).
diff --git a/scp.c b/scp.c
index 2e0dee3..82e4b99 100644
--- a/scp.c
+++ b/scp.c
@@ -78,6 +78,7 @@
#else
#include
#include
+#include
#include "win32_dirent.h"
#endif
@@ -104,6 +105,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -122,10 +124,8 @@
#include "log.h"
#include "misc.h"
#include "progressmeter.h"
+#include "utf8.h"
-#ifdef WINDOWS
-#include
-#endif
extern char *__progname;
#define COPY_BUFLEN 16384
@@ -319,125 +319,6 @@ int InitForMicrosoftWindows()
return 0;
}
-// start of direntry functions in Windows NT like UNIX
-// opendir(), readdir(), closedir().
-// NT_DIR * nt_opendir(char *name) ;
-// struct nt_dirent *nt_readdir(NT_DIR *dirp);
-// int nt_closedir(NT_DIR *dirp) ;
-
-// Windows NT directory structure content
-struct scp_dirent {
- char *d_name ; // name of the directory entry
- int d_ino; // UNIX inode
- //unsigned attrib ; // its attributes
-};
-
-typedef struct {
- long hFile;
- struct _finddata_t c_file;
-} SCPDIR;
-
-
-char * fixslashes(char * str)
-{
- int i;
- if (str == NULL)
- return str;
-
- int len = (int)strlen(str);
-
- for (i = 0; i < len; i++)
- if (str[i] == '/')
- str[i] = '\\';
- return str;
-}
-
-// force path separator to sep
-char * forcepathsep(char * str, char sep)
-{
- int i;
- // bail if str is null;
- if (str == NULL)
- return str;
-
- // bail if sep isn't valid
- if ((sep != '\\') || (sep != '/'))
- return str;
-
- char antisep = '/';
-
- if (sep == '/')
- antisep = '\\';
-
- int len = (int)strlen(str);
-
- for (i = 0; i < len; i++)
- if (str[i] == antisep)
- str[i] = sep;
- return str;
-}
-
-// get the path separator character
-char getpathsep(char * path)
-{
- char sep = '/';
- char * p = strpbrk(path,"/\\");
- if (p != NULL)
- sep = p[0];
-
- return sep;
-}
-
-bool getRootFrompath(char * path, char * root)
-{
- strcpy(root,path);
-
- char sep = getpathsep(root);
- forcepathsep(root,sep);
- char * lastslash = strrchr(root,sep);
- if (lastslash)
- *lastslash = 0x00;
- return (lastslash != NULL);
-}
-
-/*
- * get option letter from argument vector
- */
-
-char * getfilenamefrompath(char * path)
-{
- char * lastslash;
- char * lastbackslash;
-
- lastslash = strrchr(path,'/');
- lastbackslash = strrchr(path, '\\');
-
- if (lastslash == NULL && lastbackslash == NULL)
- {
- // no delimiters, just return the original string
- return path;
- }
- else if (lastslash == NULL)
- {
- // no slashes, return the backslash search minus the last char
- return ++lastbackslash;
- }
- else if (lastbackslash == NULL)
- {
- // no backslashes, return the slash search minus the last char
- return ++lastslash;
- }
- else
- {
- // string has both slashes and backslashes. return whichever is larger
- // (i.e. further down the string)
- lastslash++;
- lastbackslash++;
- return ((lastslash > lastbackslash)?lastslash:lastbackslash);
-
- }
- return NULL;
-}
#endif
#define EMSG ""
@@ -526,33 +407,6 @@ sgetopt(int nargc,
return(optopt); /* dump back option letter */
}
-
-
-/* Open a directory stream on NAME.
- Return a SCPDIR stream on the directory, or NULL if it could not be opened. */
-SCPDIR * scp_opendir(char *name)
-{
- struct _finddata_t c_file;
- long hFile;
- SCPDIR *pdir;
- char searchstr[256];
-
- sprintf_s(searchstr,sizeof(searchstr),"%s\\*.*",name); // add *.* to it for NT
-
- if( (hFile = (long)_findfirst( searchstr, &c_file )) == -1L ) {
- if ( scpverbose)
- printf( "No files found for %s search.\n", name );
- return (SCPDIR *) NULL;
- }
- else {
- pdir = (SCPDIR *) malloc( sizeof(SCPDIR) );
- pdir->hFile = hFile ;
- pdir->c_file = c_file ;
-
- return pdir ;
- }
-}
-
int _utimedir (char *name, struct _utimbuf *filetime)
{
int rc, chandle;
@@ -579,30 +433,6 @@ int _utimedir (char *name, struct _utimbuf *filetime)
HANDLE hprocess=(HANDLE) 0; // we made it a global to stop child process(ssh) of scp
#else
-/* Read a directory entry from SCPDIRP.
-Return a pointer to a `struct scp_dirent' describing the entry,
-or NULL for EOF or error. The storage returned may be overwritten
-by a later readdir call on the same SCPDIR stream. */
-struct scp_dirent *readdir(SCPDIR *dirp)
-{
- struct scp_dirent *pdirentry;
-
- for (;;) {
- if (_findnext(dirp->hFile, &(dirp->c_file)) == 0) {
- if ((strcmp(dirp->c_file.name, ".") == 0) ||
- (strcmp(dirp->c_file.name, "..") == 0)) {
- continue;
- }
- pdirentry = (struct scp_dirent *)malloc(sizeof(struct scp_dirent));
- pdirentry->d_name = dirp->c_file.name;
- pdirentry->d_ino = 1; // a fictious one like UNIX to say it is nonzero
- return pdirentry;
- }
- else {
- return (struct scp_dirent *) NULL;
- }
- }
-}
#endif
static void
@@ -645,7 +475,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");
}
#ifdef WINDOWS
@@ -941,7 +771,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);
@@ -1018,7 +848,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);
@@ -1089,12 +919,19 @@ main(int argc, char **argv)
extern int optind;
#ifdef WINDOWS
+ /*
+ * Initialize I/O wrappers.
+ */
+
+ w32posix_initialize();
ConInit(STD_OUTPUT_HANDLE, TRUE);
#endif
/* 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++)
@@ -1247,7 +1084,7 @@ main(int argc, char **argv)
exit(errs != 0);
}
if (tflag) {
- /* Receive data. */
+ /* Receive data. */
sink(argc, argv);
exit(errs != 0);
}
@@ -1569,9 +1406,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;
@@ -1607,8 +1443,6 @@ next: if (fd != -1) {
haderr = errno;
}
unset_nonblock(remout);
- if (showprogress)
- stop_progress_meter();
if (fd != -1) {
if (close(fd) < 0 && !haderr)
@@ -1620,6 +1454,8 @@ next: if (fd != -1) {
else
run_err("%s: %s", name, strerror(haderr));
(void) response();
+ if (showprogress)
+ stop_progress_meter();
}
}
@@ -1635,7 +1471,7 @@ rsource(char *name, struct stat *statp)
return;
}
last = strrchr(name, '/');
- if (last == 0)
+ if (last == NULL)
last = name;
else
last++;
@@ -1656,7 +1492,7 @@ rsource(char *name, struct stat *statp)
free(wtmp);
}
#else
- fprintf(stderr, "Entering directory: %s", path);
+ fmprintf(stderr, "Entering directory: %s", path);
#endif
(void) atomicio(vwrite, remout, path, strlen(path));
@@ -1741,7 +1577,7 @@ sink(int argc, char **argv)
free(wtmp);
}
#else
- fprintf(stderr, "Sink: %s", buf);
+ fmprintf(stderr, "Sink: %s", buf);
#endif
if (buf[0] == '\01' || buf[0] == '\02') {
if (iamremote == 0)
@@ -1922,8 +1758,6 @@ bad: run_err("%s: %s", np, strerror(errno));
}
}
unset_nonblock(remin);
- if (showprogress)
- stop_progress_meter();
if (count != 0 && wrerr == NO &&
atomicio(vwrite, ofd, bp->buf, count) != count) {
wrerr = YES;
@@ -1962,6 +1796,8 @@ bad: run_err("%s: %s", np, strerror(errno));
wrerrno = errno;
}
(void) response();
+ if (showprogress)
+ stop_progress_meter();
if (setimes && wrerr == NO) {
setimes = 0;
if (utimes(np, tv) < 0) {
@@ -2048,7 +1884,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");
}
@@ -2094,7 +1930,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/utf8.c b/utf8.c
new file mode 100644
index 0000000..f1b2fc0
--- /dev/null
+++ b/utf8.c
@@ -0,0 +1,302 @@
+/* $OpenBSD: utf8.c,v 1.3 2016/05/30 12:57:21 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 Ingo Schwarze
+ *
+ * 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 "includes.h"
+
+#include
+#ifdef HAVE_LANGINFO_H
+# include
+#endif
+#include
+#include
+#include
+#include
+#include
+#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
+# include
+#endif
+#ifdef HAVE_WCHAR_H
+# include
+#endif
+
+#include "utf8.h"
+
+static int dangerous_locale(void);
+static int grow_dst(char **, size_t *, size_t, char **, size_t);
+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) {
+
+#ifndef WINDOWS
+ char *loc;
+
+#ifdef HAVE_LANGINFO_H
+ loc = nl_langinfo(CODESET);
+#endif
+
+ return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8");
+#else
+ wchar_t loc[LOCALE_NAME_MAX_LENGTH];
+
+ GetSystemDefaultLocaleName(loc, LOCALE_NAME_MAX_LENGTH);
+
+ return wcscmp(loc, L"US-ASCII") && wcscmp(loc, L"UTF-8");
+#endif
+}
+
+static int
+grow_dst(char **dst, size_t *sz, size_t maxsz, char **dp, size_t need)
+{
+ char *tp;
+ size_t tsz;
+
+ if (*dp + need < *dst + *sz)
+ return 0;
+ tsz = *sz + 128;
+ if (tsz > maxsz)
+ tsz = maxsz;
+ if ((tp = realloc(*dst, tsz)) == NULL)
+ return -1;
+ *dp = tp + (*dp - *dst);
+ *dst = tp;
+ *sz = tsz;
+ return 0;
+}
+
+/*
+ * 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. */
+ 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 = NULL;
+ if ((ret = vasprintf(&src, fmt, ap)) <= 0)
+ goto fail;
+
+ sz = strlen(src) + 1;
+ if ((dst = malloc(sz)) == NULL) {
+ free(src);
+ 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) {
+ if (grow_dst(&dst, &sz, maxsz,
+ &dp, len) == -1) {
+ ret = -1;
+ break;
+ }
+ 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 (grow_dst(&dst, &sz, maxsz,
+ &dp, 4) == -1) {
+ ret = -1;
+ break;
+ }
+ 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:
+ if (wp != NULL)
+ *wp = 0;
+ if (ret == 0) {
+ *str = src;
+ return 0;
+ } else {
+ *str = NULL;
+ 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);
+ if (cp != NULL) {
+ (void)strlcpy(str, cp, sz);
+ free(cp);
+ } else
+ *str = '\0';
+ 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;
+}
diff --git a/utf8.h b/utf8.h
new file mode 100644
index 0000000..43ce1d5
--- /dev/null
+++ b/utf8.h
@@ -0,0 +1,24 @@
+/* $OpenBSD: utf8.h,v 1.1 2016/05/25 23:48:45 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 Ingo Schwarze
+ *
+ * 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)));