diff options
author | Ludovic Courtès <ludo@gnu.org> | 2010-02-14 22:37:39 +0100 |
---|---|---|
committer | Ludovic Courtès <ludo@gnu.org> | 2010-02-14 22:37:39 +0100 |
commit | 40ff484d80ef3bf3706262e6fcc5178e8239f60d (patch) | |
tree | cdf758e9508b3f74c117731bc9f34bed1a7b90c6 | |
parent | 55ae00ea73e5698d64c31118717c15e6dd9cafe7 (diff) | |
download | guile-40ff484d80ef3bf3706262e6fcc5178e8239f60d.tar.gz |
Add missing Gnulib files; update `.gitignore'.
This files were missing from commit
61cd9dc907b8a09990b14e9aeac7e20fe77cecc6 ("Use Gnulib's `getaddrinfo'
module.").
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | lib/gai_strerror.c | 76 | ||||
-rw-r--r-- | lib/getaddrinfo.c | 438 | ||||
-rw-r--r-- | lib/netdb.in.h | 186 | ||||
-rw-r--r-- | lib/snprintf.c | 72 |
5 files changed, 776 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore index e761f1785..b07b19618 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,7 @@ INSTALL /module/ice-9/eval.go.stamp /doc/ref/standard-library.texi /doc/ref/standard-libraryscmfiles +/lib/wchar.h +/lib/sys/socket.h +/lib/warn-on-use.h +/lib/unused-parameter.h diff --git a/lib/gai_strerror.c b/lib/gai_strerror.c new file mode 100644 index 000000000..21e80765c --- /dev/null +++ b/lib/gai_strerror.c @@ -0,0 +1,76 @@ +/* Copyright (C) 1997, 2001, 2002, 2004, 2005, 2006, 2008, 2009, 2010 Free + Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Philip Blundell <pjb27@cam.ac.uk>, 1997. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBC +# include <config.h> +#endif + +#include <stdio.h> +#include <netdb.h> + +#ifdef _LIBC +# include <libintl.h> +#else +# include "gettext.h" +# define _(String) gettext (String) +# define N_(String) String +#endif + +static struct + { + int code; + const char *msg; + } +values[] = + { + { EAI_ADDRFAMILY, N_("Address family for hostname not supported") }, + { EAI_AGAIN, N_("Temporary failure in name resolution") }, + { EAI_BADFLAGS, N_("Bad value for ai_flags") }, + { EAI_FAIL, N_("Non-recoverable failure in name resolution") }, + { EAI_FAMILY, N_("ai_family not supported") }, + { EAI_MEMORY, N_("Memory allocation failure") }, + { EAI_NODATA, N_("No address associated with hostname") }, + { EAI_NONAME, N_("Name or service not known") }, + { EAI_SERVICE, N_("Servname not supported for ai_socktype") }, + { EAI_SOCKTYPE, N_("ai_socktype not supported") }, + { EAI_SYSTEM, N_("System error") }, + { EAI_OVERFLOW, N_("Argument buffer too small") }, +#ifdef EAI_INPROGRESS + { EAI_INPROGRESS, N_("Processing request in progress") }, + { EAI_CANCELED, N_("Request canceled") }, + { EAI_NOTCANCELED, N_("Request not canceled") }, + { EAI_ALLDONE, N_("All requests done") }, + { EAI_INTR, N_("Interrupted by a signal") }, + { EAI_IDN_ENCODE, N_("Parameter string not correctly encoded") } +#endif + }; + +const char * +gai_strerror (int code) +{ + size_t i; + for (i = 0; i < sizeof (values) / sizeof (values[0]); ++i) + if (values[i].code == code) + return _(values[i].msg); + + return _("Unknown error"); +} +#ifdef _LIBC +libc_hidden_def (gai_strerror) +#endif diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c new file mode 100644 index 000000000..475eaa0f3 --- /dev/null +++ b/lib/getaddrinfo.c @@ -0,0 +1,438 @@ +/* Get address information (partial implementation). + Copyright (C) 1997, 2001-2002, 2004-2010 Free Software Foundation, Inc. + Contributed by Simon Josefsson <simon@josefsson.org>. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc + optimizes away the sa == NULL test below. */ +#define _GL_ARG_NONNULL(params) + +#include <netdb.h> + +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +/* Get inet_ntop. */ +#include <arpa/inet.h> + +/* Get calloc. */ +#include <stdlib.h> + +/* Get memcpy, strdup. */ +#include <string.h> + +/* Get snprintf. */ +#include <stdio.h> + +#include <stdbool.h> + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) String + +/* BeOS has AF_INET, but not PF_INET. */ +#ifndef PF_INET +# define PF_INET AF_INET +#endif +/* BeOS also lacks PF_UNSPEC. */ +#ifndef PF_UNSPEC +# define PF_UNSPEC 0 +#endif + +#if defined _WIN32 || defined __WIN32__ +# define WIN32_NATIVE +#endif + +#ifdef WIN32_NATIVE +typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*, + const struct addrinfo*, + struct addrinfo**); +typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*); +typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*, + socklen_t, char*, DWORD, + char*, DWORD, int); + +static getaddrinfo_func getaddrinfo_ptr = NULL; +static freeaddrinfo_func freeaddrinfo_ptr = NULL; +static getnameinfo_func getnameinfo_ptr = NULL; + +static int +use_win32_p (void) +{ + static int done = 0; + HMODULE h; + + if (done) + return getaddrinfo_ptr ? 1 : 0; + + done = 1; + + h = GetModuleHandle ("ws2_32.dll"); + + if (h) + { + getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo"); + freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo"); + getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo"); + } + + /* If either is missing, something is odd. */ + if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr) + { + getaddrinfo_ptr = NULL; + freeaddrinfo_ptr = NULL; + getnameinfo_ptr = NULL; + return 0; + } + + return 1; +} +#endif + +static inline bool +validate_family (int family) +{ + /* FIXME: Support more families. */ +#if HAVE_IPV4 + if (family == PF_INET) + return true; +#endif +#if HAVE_IPV6 + if (family == PF_INET6) + return true; +#endif + if (family == PF_UNSPEC) + return true; + return false; +} + +/* Translate name of a service location and/or a service name to set of + socket addresses. */ +int +getaddrinfo (const char *restrict nodename, + const char *restrict servname, + const struct addrinfo *restrict hints, + struct addrinfo **restrict res) +{ + struct addrinfo *tmp; + int port = 0; + struct hostent *he; + void *storage; + size_t size; +#if HAVE_IPV6 + struct v6_pair { + struct addrinfo addrinfo; + struct sockaddr_in6 sockaddr_in6; + }; +#endif +#if HAVE_IPV4 + struct v4_pair { + struct addrinfo addrinfo; + struct sockaddr_in sockaddr_in; + }; +#endif + +#ifdef WIN32_NATIVE + if (use_win32_p ()) + return getaddrinfo_ptr (nodename, servname, hints, res); +#endif + + if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE))) + /* FIXME: Support more flags. */ + return EAI_BADFLAGS; + + if (hints && !validate_family (hints->ai_family)) + return EAI_FAMILY; + + if (hints && + hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) + /* FIXME: Support other socktype. */ + return EAI_SOCKTYPE; /* FIXME: Better return code? */ + + if (!nodename) + { + if (!(hints->ai_flags & AI_PASSIVE)) + return EAI_NONAME; + +#ifdef HAVE_IPV6 + nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0"; +#else + nodename = "0.0.0.0"; +#endif + } + + if (servname) + { + struct servent *se = NULL; + const char *proto = + (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; + + if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV)) + /* FIXME: Use getservbyname_r if available. */ + se = getservbyname (servname, proto); + + if (!se) + { + char *c; + if (!(*servname >= '0' && *servname <= '9')) + return EAI_NONAME; + port = strtoul (servname, &c, 10); + if (*c || port > 0xffff) + return EAI_NONAME; + port = htons (port); + } + else + port = se->s_port; + } + + /* FIXME: Use gethostbyname_r if available. */ + he = gethostbyname (nodename); + if (!he || he->h_addr_list[0] == NULL) + return EAI_NONAME; + + switch (he->h_addrtype) + { +#if HAVE_IPV6 + case PF_INET6: + size = sizeof (struct v6_pair); + break; +#endif + +#if HAVE_IPV4 + case PF_INET: + size = sizeof (struct v4_pair); + break; +#endif + + default: + return EAI_NODATA; + } + + storage = calloc (1, size); + if (!storage) + return EAI_MEMORY; + + switch (he->h_addrtype) + { +#if HAVE_IPV6 + case PF_INET6: + { + struct v6_pair *p = storage; + struct sockaddr_in6 *sinp = &p->sockaddr_in6; + tmp = &p->addrinfo; + + if (port) + sinp->sin6_port = port; + + if (he->h_length != sizeof (sinp->sin6_addr)) + { + free (storage); + return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ + } + + memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr); + + tmp->ai_addr = (struct sockaddr *) sinp; + tmp->ai_addrlen = sizeof *sinp; + } + break; +#endif + +#if HAVE_IPV4 + case PF_INET: + { + struct v4_pair *p = storage; + struct sockaddr_in *sinp = &p->sockaddr_in; + tmp = &p->addrinfo; + + if (port) + sinp->sin_port = port; + + if (he->h_length != sizeof (sinp->sin_addr)) + { + free (storage); + return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ + } + + memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr); + + tmp->ai_addr = (struct sockaddr *) sinp; + tmp->ai_addrlen = sizeof *sinp; + } + break; +#endif + + default: + free (storage); + return EAI_NODATA; + } + + if (hints && hints->ai_flags & AI_CANONNAME) + { + const char *cn; + if (he->h_name) + cn = he->h_name; + else + cn = nodename; + + tmp->ai_canonname = strdup (cn); + if (!tmp->ai_canonname) + { + free (storage); + return EAI_MEMORY; + } + } + + tmp->ai_protocol = (hints) ? hints->ai_protocol : 0; + tmp->ai_socktype = (hints) ? hints->ai_socktype : 0; + tmp->ai_addr->sa_family = he->h_addrtype; + tmp->ai_family = he->h_addrtype; + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + switch (he->h_addrtype) + { +#if HAVE_IPV4 + case AF_INET: + tmp->ai_addr->sa_len = sizeof (struct sockaddr_in); + break; +#endif +#if HAVE_IPV6 + case AF_INET6: + tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6); + break; +#endif + } +#endif + + /* FIXME: If more than one address, create linked list of addrinfo's. */ + + *res = tmp; + + return 0; +} + +/* Free `addrinfo' structure AI including associated storage. */ +void +freeaddrinfo (struct addrinfo *ai) +{ +#ifdef WIN32_NATIVE + if (use_win32_p ()) + { + freeaddrinfo_ptr (ai); + return; + } +#endif + + while (ai) + { + struct addrinfo *cur; + + cur = ai; + ai = ai->ai_next; + + free (cur->ai_canonname); + free (cur); + } +} + +int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, + char *restrict node, socklen_t nodelen, + char *restrict service, socklen_t servicelen, + int flags) +{ +#ifdef WIN32_NATIVE + if (use_win32_p ()) + return getnameinfo_ptr (sa, salen, node, nodelen, + service, servicelen, flags); +#endif + + /* FIXME: Support other flags. */ + if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) || + (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) || + (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV))) + return EAI_BADFLAGS; + + if (sa == NULL || salen < sizeof (sa->sa_family)) + return EAI_FAMILY; + + switch (sa->sa_family) + { +#if HAVE_IPV4 + case AF_INET: + if (salen < sizeof (struct sockaddr_in)) + return EAI_FAMILY; + break; +#endif +#if HAVE_IPV6 + case AF_INET6: + if (salen < sizeof (struct sockaddr_in6)) + return EAI_FAMILY; + break; +#endif + default: + return EAI_FAMILY; + } + + if (node && nodelen > 0 && flags & NI_NUMERICHOST) + { + switch (sa->sa_family) + { +#if HAVE_IPV4 + case AF_INET: + if (!inet_ntop (AF_INET, + &(((const struct sockaddr_in *) sa)->sin_addr), + node, nodelen)) + return EAI_SYSTEM; + break; +#endif + +#if HAVE_IPV6 + case AF_INET6: + if (!inet_ntop (AF_INET6, + &(((const struct sockaddr_in6 *) sa)->sin6_addr), + node, nodelen)) + return EAI_SYSTEM; + break; +#endif + + default: + return EAI_FAMILY; + } + } + + if (service && servicelen > 0 && flags & NI_NUMERICSERV) + switch (sa->sa_family) + { +#if HAVE_IPV4 + case AF_INET: +#endif +#if HAVE_IPV6 + case AF_INET6: +#endif + { + unsigned short int port + = ntohs (((const struct sockaddr_in *) sa)->sin_port); + if (servicelen <= snprintf (service, servicelen, "%u", port)) + return EAI_OVERFLOW; + } + break; + } + + return 0; +} diff --git a/lib/netdb.in.h b/lib/netdb.in.h new file mode 100644 index 000000000..981a432b1 --- /dev/null +++ b/lib/netdb.in.h @@ -0,0 +1,186 @@ +/* Provide a netdb.h header file for systems lacking it (read: MinGW). + Copyright (C) 2008-2010 Free Software Foundation, Inc. + Written by Simon Josefsson. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* This file is supposed to be used on platforms that lack <netdb.h>. + It is intended to provide definitions and prototypes needed by an + application. */ + +#ifndef _GL_NETDB_H + +#if @HAVE_NETDB_H@ + +# if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +# endif + +/* The include_next requires a split double-inclusion guard. */ +# @INCLUDE_NEXT@ @NEXT_NETDB_H@ + +#endif + +#ifndef _GL_NETDB_H +#define _GL_NETDB_H + +/* Get netdb.h definitions such as struct hostent for MinGW. */ +#include <sys/socket.h> + +/* The definition of _GL_ARG_NONNULL is copied here. */ + +/* Declarations for a platform that lacks <netdb.h>, or where it is + incomplete. */ + +#if @GNULIB_GETADDRINFO@ + +# if !@HAVE_STRUCT_ADDRINFO@ + +/* Structure to contain information about address of a service provider. */ +struct addrinfo +{ + int ai_flags; /* Input flags. */ + int ai_family; /* Protocol family for socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol for socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address for socket. */ + char *ai_canonname; /* Canonical name for service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +# endif + +/* Possible values for `ai_flags' field in `addrinfo' structure. */ +# ifndef AI_PASSIVE +# define AI_PASSIVE 0x0001 /* Socket address is intended for `bind'. */ +# endif +# ifndef AI_CANONNAME +# define AI_CANONNAME 0x0002 /* Request for canonical name. */ +# endif +# ifndef AI_NUMERICSERV +# define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +# endif + +# if 0 +/* The commented out definitions below are not yet implemented in the + GNULIB getaddrinfo() replacement, so are not yet needed and may, in fact, + cause conflicts on systems with a getaddrinfo() function which does not + define them. + + If they are restored, be sure to protect the definitions with #ifndef. */ +# define AI_NUMERICHOST 0x0004 /* Don't use name resolution. */ +# define AI_V4MAPPED 0x0008 /* IPv4 mapped addresses are acceptable. */ +# define AI_ALL 0x0010 /* Return IPv4 mapped and IPv6 addresses. */ +# define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose + returned address type.. */ +# endif /* 0 */ + +/* Error values for `getaddrinfo' function. */ +# ifndef EAI_BADFLAGS +# define EAI_BADFLAGS -1 /* Invalid value for `ai_flags' field. */ +# define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ +# define EAI_AGAIN -3 /* Temporary failure in name resolution. */ +# define EAI_FAIL -4 /* Non-recoverable failure in name res. */ +# define EAI_NODATA -5 /* No address associated with NAME. */ +# define EAI_FAMILY -6 /* `ai_family' not supported. */ +# define EAI_SOCKTYPE -7 /* `ai_socktype' not supported. */ +# define EAI_SERVICE -8 /* SERVICE not supported for `ai_socktype'. */ +# define EAI_MEMORY -10 /* Memory allocation failure. */ +# endif + +/* Since EAI_NODATA is deprecated by RFC3493, some systems (at least + FreeBSD, which does define EAI_BADFLAGS) have removed the definition + in favor of EAI_NONAME. */ +# if !defined EAI_NODATA && defined EAI_NONAME +# define EAI_NODATA EAI_NONAME +# endif + +# ifndef EAI_OVERFLOW +/* Not defined on mingw32 and Haiku. */ +# define EAI_OVERFLOW -12 /* Argument buffer overflow. */ +# endif +# ifndef EAI_ADDRFAMILY +/* Not defined on mingw32. */ +# define EAI_ADDRFAMILY -9 /* Address family for NAME not supported. */ +# endif +# ifndef EAI_SYSTEM +/* Not defined on mingw32. */ +# define EAI_SYSTEM -11 /* System error returned in `errno'. */ +# endif + +# if 0 +/* The commented out definitions below are not yet implemented in the + GNULIB getaddrinfo() replacement, so are not yet needed. + + If they are restored, be sure to protect the definitions with #ifndef. */ +# ifndef EAI_INPROGRESS +# define EAI_INPROGRESS -100 /* Processing request in progress. */ +# define EAI_CANCELED -101 /* Request canceled. */ +# define EAI_NOTCANCELED -102 /* Request not canceled. */ +# define EAI_ALLDONE -103 /* All requests done. */ +# define EAI_INTR -104 /* Interrupted by a signal. */ +# define EAI_IDN_ENCODE -105 /* IDN encoding failed. */ +# endif +# endif + +# if !@HAVE_DECL_GETADDRINFO@ +/* Translate name of a service location and/or a service name to set of + socket addresses. + For more details, see the POSIX:2001 specification + <http://www.opengroup.org/susv3xsh/getaddrinfo.html>. */ +extern int getaddrinfo (const char *restrict nodename, + const char *restrict servname, + const struct addrinfo *restrict hints, + struct addrinfo **restrict res) + _GL_ARG_NONNULL ((4)); +# endif + +# if !@HAVE_DECL_FREEADDRINFO@ +/* Free `addrinfo' structure AI including associated storage. + For more details, see the POSIX:2001 specification + <http://www.opengroup.org/susv3xsh/getaddrinfo.html>. */ +extern void freeaddrinfo (struct addrinfo *ai) _GL_ARG_NONNULL ((1)); +# endif + +# if !@HAVE_DECL_GAI_STRERROR@ +/* Convert error return from getaddrinfo() to a string. + For more details, see the POSIX:2001 specification + <http://www.opengroup.org/susv3xsh/gai_strerror.html>. */ +extern const char *gai_strerror (int ecode); +# endif + +# if !@HAVE_DECL_GETNAMEINFO@ +/* Convert socket address to printable node and service names. + For more details, see the POSIX:2001 specification + <http://www.opengroup.org/susv3xsh/getnameinfo.html>. */ +extern int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, + char *restrict node, socklen_t nodelen, + char *restrict service, socklen_t servicelen, + int flags) + _GL_ARG_NONNULL ((1)); +# endif + +/* Possible flags for getnameinfo. */ +# ifndef NI_NUMERICHOST +# define NI_NUMERICHOST 1 +# endif +# ifndef NI_NUMERICSERV +# define NI_NUMERICSERV 2 +# endif + +#endif /* @GNULIB_GETADDRINFO@ */ + +#endif /* _GL_NETDB_H */ +#endif /* _GL_NETDB_H */ diff --git a/lib/snprintf.c b/lib/snprintf.c new file mode 100644 index 000000000..424367934 --- /dev/null +++ b/lib/snprintf.c @@ -0,0 +1,72 @@ +/* Formatted output to strings. + Copyright (C) 2004, 2006-2010 Free Software Foundation, Inc. + Written by Simon Josefsson and Paul Eggert. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +/* Specification. */ +#include <stdio.h> + +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "vasnprintf.h" + +/* Print formatted output to string STR. Similar to sprintf, but + additional length SIZE limit how much is written into STR. Returns + string length of formatted string (which may be larger than SIZE). + STR may be NULL, in which case nothing will be written. On error, + return a negative value. */ +int +snprintf (char *str, size_t size, const char *format, ...) +{ + char *output; + size_t len; + size_t lenbuf = size; + va_list args; + + va_start (args, format); + output = vasnprintf (str, &lenbuf, format, args); + len = lenbuf; + va_end (args); + + if (!output) + return -1; + + if (output != str) + { + if (size) + { + size_t pruned_len = (len < size ? len : size - 1); + memcpy (str, output, pruned_len); + str[pruned_len] = '\0'; + } + + free (output); + } + + if (INT_MAX < len) + { + errno = EOVERFLOW; + return -1; + } + + return len; +} |