]> granicus.if.org Git - file/commitdiff
Replace use of (v)snprintf with (v)asprintf. This has several advantages:
authorReuben Thomas <rrt@sc3d.org>
Tue, 19 Feb 2008 00:58:59 +0000 (00:58 +0000)
committerReuben Thomas <rrt@sc3d.org>
Tue, 19 Feb 2008 00:58:59 +0000 (00:58 +0000)
1. (Mostly) easier to program with as the allocation is done for you.
   (Sometimes it's a little more complicated to get the deallocation
   right). The amount of code removed as a result by this commit is
   greater than the amount introduced (excluding the (v)asprintf
   implementation).

2. Safer: no buffer overflows.

3. Works on all platforms: BSD-licensed mature xnprintf code used
   (I've been using this for some years in Zile with no problems). No
   more overflows due to the previous bogus snprintf/vsnprintf
   fallbacks.

Also add and use a MAX() macro in the same vein as MIN().

configure.ac
src/apprentice.c
src/asprintf.c [new file with mode: 0644]
src/file.h
src/funcs.c
src/magic.c
src/softmagic.c
src/vasprintf.c [new file with mode: 0644]

index 76869e7b0c2c54167f53a1238005a3b4e80ee904..8ad8dc3e08e6c7cd84e79a8f3259c94ab235d342 100644 (file)
@@ -107,10 +107,10 @@ typedef long int64_t;
 ])\r
 \r
 dnl Checks for functions\r
-AC_CHECK_FUNCS(mmap strerror strndup strtoul mbrtowc mkstemp utimes utime wcwidth snprintf vsnprintf strtof)\r
+AC_CHECK_FUNCS(mmap strerror strndup strtoul mbrtowc mkstemp utimes utime wcwidth strtof)\r
 \r
 dnl Provide implementation of some required functions if necessary\r
-AC_REPLACE_FUNCS(getopt_long)\r
+AC_REPLACE_FUNCS(getopt_long asprintf vasprintf)\r
 \r
 dnl Checks for libraries\r
 AC_CHECK_LIB(z,gzopen)\r
index 70a666ec19c11f429edc034791a61c28f644ae98..00c18471a5617dcb03dcc67f8ddc421189ee6e1a 100644 (file)
@@ -47,7 +47,7 @@
 #endif
 
 #ifndef        lint
-FILE_RCSID("@(#)$File: apprentice.c,v 1.120 2008/02/18 21:45:58 rrt Exp $")
+FILE_RCSID("@(#)$File: apprentice.c,v 1.121 2008/02/18 21:50:24 rrt Exp $")
 #endif /* lint */
 
 #define        EATAB {while (isascii((unsigned char) *l) && \
@@ -106,7 +106,7 @@ private void bs1(struct magic *);
 private uint16_t swap2(uint16_t);
 private uint32_t swap4(uint32_t);
 private uint64_t swap8(uint64_t);
-private char *mkdbname(const char *, char *, size_t, int);
+private void mkdbname(const char *, char **, int);
 private int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
     const char *);
 private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
@@ -360,8 +360,7 @@ file_apprentice(struct magic_set *ms, const char *fn, int action)
                if (*fn == '\0')
                        break;
                file_err = apprentice_1(ms, fn, action, mlist);
-               if (file_err > errs)
-                       errs = file_err;
+               errs = MAX(errs, file_err);
                fn = p;
        }
        if (errs == -1) {
@@ -1765,40 +1764,40 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
        uint32_t *ptr;
        uint32_t version;
        int needsbyteswap;
-       char buf[MAXPATHLEN];
-       char *dbname = mkdbname(fn, buf, sizeof(buf), 0);
+       char *dbname = NULL;
        void *mm = NULL;
 
+       mkdbname(fn, &dbname, 0);
        if (dbname == NULL)
-               return -1;
+               goto error2;
 
        if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
-               return -1;
+               goto error2;
 
        if (fstat(fd, &st) == -1) {
                file_error(ms, errno, "cannot stat `%s'", dbname);
-               goto error;
+               goto error1;
        }
        if (st.st_size < 8) {
                file_error(ms, 0, "file `%s' is too small", dbname);
-               goto error;
+               goto error1;
        }
 
 #ifdef QUICK
        if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
            MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
                file_error(ms, errno, "cannot map `%s'", dbname);
-               goto error;
+               goto error1;
        }
 #define RET    2
 #else
        if ((mm = malloc((size_t)st.st_size)) == NULL) {
                file_oomem(ms, (size_t)st.st_size);
-               goto error;
+               goto error1;
        }
        if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) {
                file_badread(ms);
-               goto error;
+               goto error1;
        }
 #define RET    1
 #endif
@@ -1809,7 +1808,7 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
        if (*ptr != MAGICNO) {
                if (swap4(*ptr) != MAGICNO) {
                        file_error(ms, 0, "bad magic in `%s'");
-                       goto error;
+                       goto error1;
                }
                needsbyteswap = 1;
        } else
@@ -1822,7 +1821,7 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
                file_error(ms, 0, "File %d.%d supports only %d version magic "
                    "files. `%s' is version %d", FILE_VERSION_MAJOR, patchlevel,
                    VERSIONNO, dbname, version);
-               goto error;
+               goto error1;
        }
        *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic));
        if (*nmagicp > 0)
@@ -1830,9 +1829,10 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
        (*magicp)++;
        if (needsbyteswap)
                byteswap(*magicp, *nmagicp);
+       free(dbname);
        return RET;
 
-error:
+error1:
        if (fd != -1)
                (void)close(fd);
        if (mm) {
@@ -1845,6 +1845,8 @@ error:
                *magicp = NULL;
                *nmagicp = 0;
        }
+error2:
+       free(dbname);
        return -1;
 }
 
@@ -1859,44 +1861,49 @@ apprentice_compile(struct magic_set *ms, struct magic **magicp,
     uint32_t *nmagicp, const char *fn)
 {
        int fd;
-       char buf[MAXPATHLEN];
-       char *dbname = mkdbname(fn, buf, sizeof(buf), 1);
+       char *dbname;
+       int rv = -1;
+
+       mkdbname(fn, &dbname, 1);
 
        if (dbname == NULL) 
-               return -1;
+               goto out;
 
        if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) {
                file_error(ms, errno, "cannot open `%s'", dbname);
-               return -1;
+               goto out;
        }
 
        if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
                file_error(ms, errno, "error writing `%s'", dbname);
-               return -1;
+               goto out;
        }
 
        if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET)
            != sizeof(struct magic)) {
                file_error(ms, errno, "error seeking `%s'", dbname);
-               return -1;
+               goto out;
        }
 
        if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) 
            != (ssize_t)(sizeof(struct magic) * *nmagicp)) {
                file_error(ms, errno, "error writing `%s'", dbname);
-               return -1;
+               goto out;
        }
 
        (void)close(fd);
-       return 0;
+       rv = 0;
+out:
+       free(dbname);
+       return rv;
 }
 
 private const char ext[] = ".mgc";
 /*
  * make a dbname
  */
-private char *
-mkdbname(const char *fn, char *buf, size_t bufsiz, int strip)
+private void
+mkdbname(const char *fn, char **buf, int strip)
 {
        if (strip) {
                const char *p;
@@ -1904,8 +1911,11 @@ mkdbname(const char *fn, char *buf, size_t bufsiz, int strip)
                        fn = ++p;
        }
 
-       (void)snprintf(buf, bufsiz, "%s%s", fn, ext);
-       return buf;
+       (void)asprintf(buf, "%s%s", fn, ext);
+       if (*buf && strlen(*buf) > MAXPATHLEN) {
+               free(*buf);
+               *buf = NULL;
+       }
 }
 
 /*
diff --git a/src/asprintf.c b/src/asprintf.c
new file mode 100644 (file)
index 0000000..c103cf1
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice immediately at the beginning of the file, without modification,
+ *    this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+
+int vasprintf(char **ptr, const char *format_string, va_list vargs);
+
+int asprintf(char **ptr, const char *fmt, ...)
+{
+  va_list vargs;
+  int retval;
+
+  va_start(vargs, fmt);
+  retval = vasprintf(ptr, fmt, vargs);
+  va_end(vargs);
+
+  return retval;
+}
index bfcc4e2b9f4df6dfee5d1d6cf8e1140810efbc35..0fae7d749530c4777d5474b73dfa1ea14fbae71e 100644 (file)
@@ -27,7 +27,7 @@
  */
 /*
  * file.h - definitions for file(1) program
- * @(#)$File: file.h,v 1.95 2008/02/12 16:30:48 rrt Exp $
+ * @(#)$File: file.h,v 1.96 2008/02/17 19:28:54 rrt Exp $
  */
 
 #ifndef __file_h__
@@ -50,6 +50,7 @@
 #include <sys/types.h>
 /* Do this here and now, because struct stat gets re-defined on solaris */
 #include <sys/stat.h>
+#include <stdarg.h>
 
 #define ENABLE_CONDITIONALS
 
 #define        MIN(a,b)        (((a) < (b)) ? (a) : (b))
 #endif
 
+#ifndef MAX
+#define        MAX(a,b)        (((a) > (b)) ? (a) : (b))
+#endif
+
 #ifndef HOWMANY
 # define HOWMANY (256 * 1024)  /* how much of the file to look at */
 #endif
@@ -280,14 +285,8 @@ struct magic_set {
                } *li;
        } c;
        struct out {
-               /* Accumulation buffer */
-               char *buf;
-               char *ptr;
-               size_t left;
-               size_t size;
-               /* Printable buffer */
-               char *pbuf;
-               size_t psize;
+               char *buf;              /* Accumulation buffer */
+               char *pbuf;             /* Printable buffer */
        } o;
        uint32_t offset;
        int error;
@@ -357,8 +356,11 @@ extern char *sys_errlist[];
 #define strtoul(a, b, c)       strtol(a, b, c)
 #endif
 
-#ifndef HAVE_SNPRINTF
-int snprintf(char *, size_t, const char *, ...);
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **ptr, const char *format_string, va_list vargs);
+#endif
+#ifndef HAVE_ASPRINTF
+int asprintf(char **ptr, const char *format_string, ...);
 #endif
 
 #if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H) && !defined(QUICK)
index b0ce090ea23dc1bde00805c314327daa69126366..67f01cebef186ab04cdd8b8288e75a2cb3ab18c3 100644 (file)
 #endif
 
 #ifndef        lint
-FILE_RCSID("@(#)$File: funcs.c,v 1.36 2008/02/04 16:33:46 christos Exp $")
+FILE_RCSID("@(#)$File: funcs.c,v 1.37 2008/02/07 00:58:52 christos Exp $")
 #endif /* lint */
 
-#ifndef HAVE_VSNPRINTF
-int vsnprintf(char *, size_t, const char *, va_list);
-#endif
-
 /*
- * Like printf, only we print to a buffer and advance it.
+ * Like printf, only we append to a buffer.
  */
 protected int
 file_printf(struct magic_set *ms, const char *fmt, ...)
 {
        va_list ap;
        size_t size;
-       ssize_t len;
-       char *buf;
+       int len;
+       char *buf, *newstr;
 
        va_start(ap, fmt);
-
-       len = vsnprintf(ms->o.ptr, ms->o.left, fmt, ap);
-       if (len == -1)
+       len = vasprintf(&buf, fmt, ap);
+       if (len < 0)
                goto out;
-       if (len >= (ssize_t)ms->o.left) {
-               long diff;      /* XXX: really ptrdiff_t */
-
-               va_end(ap);
-               size = (ms->o.size - ms->o.left) + len + 1024;
-               if ((buf = realloc(ms->o.buf, size)) == NULL) {
-                       file_oomem(ms, size);
-                       return -1;
-               }
-               diff = ms->o.ptr - ms->o.buf;
-               ms->o.ptr = buf + diff;
-               ms->o.buf = buf;
-               ms->o.left = size - diff;
-               ms->o.size = size;
+       va_end(ap);
 
-               va_start(ap, fmt);
-               len = vsnprintf(ms->o.ptr, ms->o.left, fmt, ap);
-               if (len == -1)
+       if (ms->o.buf != NULL) {
+               len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
+               free(buf);
+               if (len < 0)
                        goto out;
+               free(ms->o.buf);
+               buf = newstr;
        }
-       va_end(ap);
-       ms->o.ptr += len;
-       ms->o.left -= len;
+       ms->o.buf = buf;
        return 0;
 out:
-       file_error(ms, errno, "vsnprintf failed");
+       file_error(ms, errno, "vasprintf failed");
        return -1;
 }
 
@@ -98,21 +81,17 @@ private void
 file_error_core(struct magic_set *ms, int error, const char *f, va_list va,
     uint32_t lineno)
 {
-       size_t len;
        /* Only the first error is ok */
        if (ms->haderr)
                return;
-       len = 0;
        if (lineno != 0) {
-               (void)snprintf(ms->o.buf, ms->o.size, "line %u: ", lineno);
-               len = strlen(ms->o.buf);
-       }
-       (void)vsnprintf(ms->o.buf + len, ms->o.size - len, f, va);
-       if (error > 0) {
-               len = strlen(ms->o.buf);
-               (void)snprintf(ms->o.buf + len, ms->o.size - len, " (%s)",
-                   strerror(error));
+               free(ms->o.buf);
+               ms->o.buf = NULL;
+               file_printf(ms, "line %u: ", lineno);
        }
+        file_printf(ms, f, va);
+       if (error > 0)
+               file_printf(ms, " (%s)", strerror(error));
        ms->haderr++;
        ms->error = error;
 }
@@ -240,8 +219,7 @@ file_reset(struct magic_set *ms)
                file_error(ms, 0, "no magic files loaded");
                return -1;
        }
-       ms->o.ptr = ms->o.buf;
-       ms->o.left = ms->o.size;
+       ms->o.buf = NULL;
        ms->haderr = 0;
        ms->error = -1;
        return 0;
@@ -267,21 +245,18 @@ file_getbuffer(struct magic_set *ms)
        if (ms->flags & MAGIC_RAW)
                return ms->o.buf;
 
-       len = ms->o.size - ms->o.left;
        /* * 4 is for octal representation, + 1 is for NUL */
+       len = strlen(ms->o.buf);
        if (len > (SIZE_MAX - 1) / 4) {
                file_oomem(ms, len);
                return NULL;
        }
        psize = len * 4 + 1;
-       if (ms->o.psize < psize) {
-               if ((pbuf = realloc(ms->o.pbuf, psize)) == NULL) {
-                       file_oomem(ms, psize);
-                       return NULL;
-               }
-               ms->o.psize = psize;
-               ms->o.pbuf = pbuf;
+       if ((pbuf = realloc(ms->o.pbuf, psize)) == NULL) {
+               file_oomem(ms, psize);
+               return NULL;
        }
+       ms->o.pbuf = pbuf;
 
 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
        {
@@ -294,7 +269,7 @@ file_getbuffer(struct magic_set *ms)
 
                np = ms->o.pbuf;
                op = ms->o.buf;
-               eop = op + strlen(ms->o.buf);
+               eop = op + len;
 
                while (op < eop) {
                        bytesconsumed = mbrtowc(&nextchar, op,
@@ -354,28 +329,3 @@ file_check_mem(struct magic_set *ms, unsigned int level)
 #endif /* ENABLE_CONDITIONALS */
        return 0;
 }
-/*
- * Yes these wrappers suffer from buffer overflows, but if your OS does not
- * have the real functions, maybe you should consider replacing your OS?
- */
-#ifndef HAVE_VSNPRINTF
-int
-vsnprintf(char *buf, size_t len, const char *fmt, va_list ap)
-{
-       return vsprintf(buf, fmt, ap);
-}
-#endif
-
-#ifndef HAVE_SNPRINTF
-/*ARGSUSED*/
-int
-snprintf(char *buf, size_t len, const char *fmt, ...)
-{
-       int rv;
-       va_list ap;
-       va_start(ap, fmt);
-       rv = vsprintf(buf, fmt, ap);
-       va_end(ap);
-       return rv;
-}
-#endif
index ee1cdbe6fedf5c94d0313aa36aa34b067f659bc6..8ecbb8f0da87baf17aac8fac67c323bf85efe7db 100644 (file)
@@ -63,7 +63,7 @@
 #include "patchlevel.h"
 
 #ifndef        lint
-FILE_RCSID("@(#)$File: magic.c,v 1.48 2008/02/07 00:58:52 christos Exp $")
+FILE_RCSID("@(#)$File: magic.c,v 1.49 2008/02/17 19:28:54 rrt Exp $")
 #endif /* lint */
 
 #ifndef PIPE_BUF 
@@ -103,20 +103,14 @@ magic_open(int flags)
 
        if (magic_setflags(ms, flags) == -1) {
                errno = EINVAL;
-               goto free1;
+               goto free;
        }
 
-       ms->o.ptr = ms->o.buf = malloc(ms->o.left = ms->o.size = 1024);
-       if (ms->o.buf == NULL)
-               goto free1;
-
-       ms->o.pbuf = malloc(ms->o.psize = 1024);
-       if (ms->o.pbuf == NULL)
-               goto free2;
+       ms->o.buf = ms->o.pbuf = NULL;
 
        ms->c.li = malloc((ms->c.len = 10) * sizeof(*ms->c.li));
        if (ms->c.li == NULL)
-               goto free3;
+               goto free;
        
        ms->haderr = 0;
        ms->error = -1;
@@ -124,11 +118,7 @@ magic_open(int flags)
        ms->file = "unknown";
        ms->line = 0;
        return ms;
-free3:
-       free(ms->o.pbuf);
-free2:
-       free(ms->o.buf);
-free1:
+free:
        free(ms);
        return NULL;
 }
index e68579a3dd40b060c5c6afa5c19f6543696cd923..9926acd8295b66f8316952d52d436c18e74973ca 100644 (file)
@@ -38,7 +38,7 @@
 
 
 #ifndef        lint
-FILE_RCSID("@(#)$File: softmagic.c,v 1.110 2008/02/17 19:28:54 rrt Exp $")
+FILE_RCSID("@(#)$File: softmagic.c,v 1.111 2008/02/18 00:43:45 rrt Exp $")
 #endif /* lint */
 
 private int match(struct magic_set *, struct magic *, uint32_t,
@@ -321,7 +321,7 @@ mprint(struct magic_set *ms, struct magic *m)
        float vf;
        double vd;
        int64_t t = 0;
-       char buf[512];
+       char *buf;
        union VALUETYPE *p = &ms->ms_value;
 
        switch (m->type) {
@@ -331,8 +331,7 @@ mprint(struct magic_set *ms, struct magic *m)
                case -1:
                        return -1;
                case 1:
-                       if (snprintf(buf, sizeof(buf), "%c",
-                           (unsigned char)v) < 0)
+                       if (asprintf(&buf, "%c", (unsigned char)v) < 0)
                                return -1;
                        if (file_printf(ms, MAGIC_DESC, buf) == -1)
                                return -1;
@@ -353,8 +352,7 @@ mprint(struct magic_set *ms, struct magic *m)
                case -1:
                        return -1;
                case 1:
-                       if (snprintf(buf, sizeof(buf), "%hu",
-                           (unsigned short)v) < 0)
+                       if (asprintf(&buf, "%hu", (unsigned short)v) < 0)
                                return -1;
                        if (file_printf(ms, MAGIC_DESC, buf) == -1)
                                return -1;
@@ -376,7 +374,7 @@ mprint(struct magic_set *ms, struct magic *m)
                case -1:
                        return -1;
                case 1:
-                       if (snprintf(buf, sizeof(buf), "%u", (uint32_t)v) < 0)
+                       if (asprintf(&buf, "%u", (uint32_t)v) < 0)
                                return -1;
                        if (file_printf(ms, MAGIC_DESC, buf) == -1)
                                return -1;
@@ -462,7 +460,7 @@ mprint(struct magic_set *ms, struct magic *m)
                case -1:
                        return -1;
                case 1:
-                       if (snprintf(buf, sizeof(buf), "%g", vf) < 0)
+                       if (asprintf(&buf, "%g", vf) < 0)
                                return -1;
                        if (file_printf(ms, MAGIC_DESC, buf) == -1)
                                return -1;
@@ -483,7 +481,7 @@ mprint(struct magic_set *ms, struct magic *m)
                case -1:
                        return -1;
                case 1:
-                       if (snprintf(buf, sizeof(buf), "%g", vd) < 0)
+                       if (asprintf(&buf, "%g", vd) < 0)
                                return -1;
                        if (file_printf(ms, MAGIC_DESC, buf) == -1)
                                return -1;
diff --git a/src/vasprintf.c b/src/vasprintf.c
new file mode 100644 (file)
index 0000000..acb6eda
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice immediately at the beginning of the file, without modification,
+ *    this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*###########################################################################
+  #                                                                           #
+  #                                vasprintf                                  #
+  #                                                                           #
+  #               Copyright (c) 2002-2005 David TAILLANDIER                   #
+  #                                                                           #
+  ###########################################################################*/
+
+/*
+
+This software is distributed under the "modified BSD licence".
+
+This software is also released with GNU license (GPL) in another file (same
+source-code, only license differ).
+
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer. Redistributions in binary
+form must reproduce the above copyright notice, this list of conditions and
+the following disclaimer in the documentation and/or other materials
+provided with the distribution. The name of the author may not be used to
+endorse or promote products derived from this software without specific
+prior written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+====================
+
+Hacked from xnprintf version of 26th February 2005 to provide only
+vasprintf by Reuben Thomas <rrt@sc3d.org>.
+
+====================
+
+
+'printf' function family use the following format string:
+
+%[flag][width][.prec][modifier]type
+
+%% is the escape sequence to print a '%'
+%  followed by an unknown format will print the characters without
+trying to do any interpretation
+
+flag:   none   +     -     #     (blank)
+width:  n    0n    *
+prec:   none   .0    .n     .*
+modifier:    F N L h l    ('F' and 'N' are ms-dos/16-bit specific)
+type:  d i o u x X f e g E G c s p n
+
+
+The function needs to allocate memory to store the full text before to
+actually writting it.  i.e if you want to fnprintf() 1000 characters, the
+functions will allocate 1000 bytes.
+This behaviour can be modified: you have to customise the code to flush the
+internal buffer (writing to screen or file) when it reach a given size. Then
+the buffer can have a shorter length. But what? If you really need to write
+HUGE string, don't use printf!
+During the process, some other memory is allocated (1024 bytes minimum)
+to handle the output of partial sprintf() calls. If you have only 10000 bytes
+free in memory, you *may* not be able to nprintf() a 8000 bytes-long text.
+
+note: if a buffer overflow occurs, exit() is called. This situation should
+never appear ... but if you want to be *really* sure, you have to modify the
+code to handle those situations (only one place to modify).
+A buffer overflow can only occur if your sprintf() do strange things or when
+you use strange formats.
+
+*/
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <limits.h>
+
+#define ALLOC_CHUNK 2048
+#define ALLOC_SECURITY_MARGIN 1024   /* big value because some platforms have very big 'G' exponent */
+#if ALLOC_CHUNK < ALLOC_SECURITY_MARGIN
+#    error  !!! ALLOC_CHUNK < ALLOC_SECURITY_MARGIN !!!
+#endif
+/* note: to have some interest, ALLOC_CHUNK should be much greater than ALLOC_SECURITY_MARGIN */
+
+/*
+ *  To save a lot of push/pop, every variable are stored into this
+ *  structure, which is passed among nearly every sub-functions.
+ */
+typedef struct {
+  const char * src_string;        /* current position into intput string */
+  char *       buffer_base;       /* output buffer */
+  char *       dest_string;       /* current position into output string */
+  size_t       buffer_len;        /* length of output buffer */
+  size_t       real_len;          /* real current length of output text */
+  size_t       pseudo_len;        /* total length of output text if it were not limited in size */
+  size_t       maxlen;
+  va_list      vargs;             /* pointer to current position into vargs */
+  char *       sprintf_string;
+  FILE *       fprintf_file;
+} xprintf_struct;
+
+/*
+ *  Realloc buffer if needed
+ *  Return value:  0 = ok
+ *               EOF = not enought memory
+ */
+static int realloc_buff(xprintf_struct *s, size_t len)
+{
+  char * ptr;
+
+  if (len + ALLOC_SECURITY_MARGIN + s->real_len > s->buffer_len) {
+    len += s->real_len + ALLOC_CHUNK;
+    ptr = (char *)realloc((void *)(s->buffer_base), len);
+    if (ptr == NULL) {
+      s->buffer_base = NULL;
+      return EOF;
+    }
+
+    s->dest_string = ptr + (size_t)(s->dest_string - s->buffer_base);
+    s->buffer_base = ptr;
+    s->buffer_len = len;
+
+    (s->buffer_base)[s->buffer_len - 1] = 1; /* overflow marker */
+  }
+
+  return 0;
+}
+
+/*
+ *  Prints 'usual' characters    up to next '%'
+ *                            or up to end of text
+ */
+static int usual_char(xprintf_struct * s)
+{
+  size_t len;
+
+  len = strcspn(s->src_string, "%");     /* reachs the next '%' or end of input string */
+  /* note: 'len' is never 0 because the presence of '%' */
+  /* or end-of-line is checked in the calling function  */
+
+  if (realloc_buff(s,len) == EOF)
+    return EOF;
+
+  memcpy(s->dest_string, s->src_string, len);
+  s->src_string += len;
+  s->dest_string += len;
+  s->real_len += len;
+  s->pseudo_len += len;
+
+  return 0;
+}
+
+/*
+ *  Return value: 0 = ok
+ *                EOF = error
+ */
+static int print_it(xprintf_struct *s, size_t approx_len,
+                    const char *format_string, ...)
+{
+  va_list varg;
+  int vsprintf_len;
+  size_t len;
+
+  if (realloc_buff(s,approx_len) == EOF)
+    return EOF;
+
+  va_start(varg, format_string);
+  vsprintf_len = vsprintf(s->dest_string, format_string, varg);
+  va_end(varg);
+
+  /* Check for overflow */
+  assert((s->buffer_base)[s->buffer_len - 1] == 1);
+
+  if (vsprintf_len == EOF) /* must be done *after* overflow-check */
+    return EOF;
+
+  s->pseudo_len += vsprintf_len;
+  len = strlen(s->dest_string);
+  s->real_len += len;
+  s->dest_string += len;
+
+  return 0;
+}
+
+/*
+ *  Prints a string (%s)
+ *  We need special handling because:
+ *     a: the length of the string is unknown
+ *     b: when .prec is used, we must not access any extra byte of the
+ *        string (of course, if the original sprintf() does... what the
+ *        hell, not my problem)
+ *
+ *  Return value: 0 = ok
+ *                EOF = error
+ */
+static int type_s(xprintf_struct *s, int width, int prec,
+                  const char *format_string, const char *arg_string)
+{
+  size_t string_len;
+
+  if (arg_string == NULL)
+    return print_it(s, (size_t)6, "(null)", 0);
+
+  /* hand-made strlen() whitch stops when 'prec' is reached. */
+  /* if 'prec' is -1 then it is never reached. */
+  string_len = 0;
+  while (arg_string[string_len] != 0 && (size_t)prec != string_len)
+    string_len++;
+
+  if (width != -1 && string_len < (size_t)width)
+    string_len = (size_t)width;
+
+  return print_it(s, string_len, format_string, arg_string);
+}
+
+/*
+ *  Read a serie of digits. Stop when non-digit is found.
+ *  Return value: the value read (between 0 and 32767).
+ *  Note: no checks are made against overflow. If the string contain a big
+ *  number, then the return value won't be what we want (but, in this case,
+ *  the programmer don't know whatr he wants, then no problem).
+ */
+static int getint(const char **string)
+{
+  int i = 0;
+
+  while (isdigit(**string) != 0) {
+    i = i * 10 + (**string - '0');
+    (*string)++;
+  }
+
+  if (i < 0 || i > 32767)
+    i = 32767; /* if we have i==-10 this is not because the number is */
+  /* negative; this is because the number is big */
+  return i;
+}
+
+/*
+ *  Read a part of the format string. A part is 'usual characters' (ie "blabla")
+ *  or '%%' escape sequence (to print a single '%') or any combination of
+ *  format specifier (ie "%i" or "%10.2d").
+ *  After the current part is managed, the function returns to caller with
+ *  everything ready to manage the following part.
+ *  The caller must ensure than the string is not empty, i.e. the first byte
+ *  is not zero.
+ *
+ *  Return value:  0 = ok
+ *                 EOF = error
+ */
+static int dispatch(xprintf_struct *s)
+{
+  const char *initial_ptr;
+  char format_string[24]; /* max length may be something like  "% +-#032768.32768Ld" */
+  char *format_ptr;
+  int flag_plus, flag_minus, flag_space, flag_sharp, flag_zero;
+  int width, prec, modifier, approx_width;
+  char type;
+  /* most of those variables are here to rewrite the format string */
+
+#define SRCTXT  (s->src_string)
+#define DESTTXT (s->dest_string)
+
+  /* incoherent format string. Characters after the '%' will be printed with the next call */
+#define INCOHERENT()         do {SRCTXT=initial_ptr; return 0;} while (0)     /* do/while to avoid */
+#define INCOHERENT_TEST()    do {if(*SRCTXT==0)   INCOHERENT();} while (0)    /* a null statement  */
+
+  /* 'normal' text */
+  if (*SRCTXT != '%')
+    return usual_char(s);
+
+  /* we then have a '%' */
+  SRCTXT++;
+  /* don't check for end-of-string ; this is done later */
+
+  /* '%%' escape sequence */
+  if (*SRCTXT == '%') {
+    if (realloc_buff(s, (size_t)1) == EOF) /* because we can have "%%%%%%%%..." */
+      return EOF;
+    *DESTTXT = '%';
+    DESTTXT++;
+    SRCTXT++;
+    (s->real_len)++;
+    (s->pseudo_len)++;
+    return 0;
+  }
+
+  /* '%' managing */
+  initial_ptr = SRCTXT;   /* save current pointer in case of incorrect */
+  /* 'decoding'. Points just after the '%' so the '%' */
+  /* won't be printed in any case, as required. */
+
+  /* flag */
+  flag_plus = flag_minus = flag_space = flag_sharp = flag_zero = 0;
+
+  for (;; SRCTXT++) {
+    if (*SRCTXT == ' ')
+      flag_space = 1;
+    else if (*SRCTXT == '+')
+      flag_plus = 1;
+    else if (*SRCTXT == '-')
+      flag_minus = 1;
+    else if (*SRCTXT == '#')
+      flag_sharp = 1;
+    else if (*SRCTXT == '0')
+      flag_zero = 1;
+    else
+      break;
+  }
+
+  INCOHERENT_TEST();    /* here is the first test for end of string */
+
+  /* width */
+  if (*SRCTXT == '*') {         /* width given by next argument */
+    SRCTXT++;
+    width = va_arg(s->vargs, int);
+    if ((size_t)width > 0x3fffU) /* 'size_t' to check against negative values too */
+      width = 0x3fff;
+  } else if (isdigit(*SRCTXT)) /* width given as ASCII number */
+    width = getint(&SRCTXT);
+  else
+    width = -1;                 /* no width specified */
+
+  INCOHERENT_TEST();
+
+  /* .prec */
+  if (*SRCTXT == '.') {
+    SRCTXT++;
+    if (*SRCTXT == '*') {       /* .prec given by next argument */
+      prec = va_arg(s->vargs, int);
+      if ((size_t)prec >= 0x3fffU) /* 'size_t' to check against negative values too */
+        prec = 0x3fff;
+    } else {                    /* .prec given as ASCII number */
+      if (isdigit(*SRCTXT) == 0)
+        INCOHERENT();
+      prec = getint(&SRCTXT);
+    }
+    INCOHERENT_TEST();
+  } else
+    prec = -1;                  /* no .prec specified */
+
+  /* modifier */
+  if (*SRCTXT == 'L' || *SRCTXT == 'h' || *SRCTXT == 'l') {
+    modifier = *SRCTXT;
+    SRCTXT++;
+    if (modifier=='l' && *SRCTXT=='l') {
+      SRCTXT++;
+      modifier = 'L';  /* 'll' == 'L'      long long == long double */
+    } /* only for compatibility ; not portable */
+    INCOHERENT_TEST();
+  } else
+    modifier = -1;              /* no modifier specified */
+
+  /* type */
+  type = *SRCTXT;
+  if (strchr("diouxXfegEGcspn",type) == NULL)
+    INCOHERENT();               /* unknown type */
+  SRCTXT++;
+
+  /* rewrite format-string */
+  format_string[0] = '%';
+  format_ptr = &(format_string[1]);
+
+  if (flag_plus) {
+    *format_ptr = '+';
+    format_ptr++;
+  }
+  if (flag_minus) {
+    *format_ptr = '-';
+    format_ptr++;
+  }
+  if (flag_space) {
+    *format_ptr = ' ';
+    format_ptr++;
+  }
+  if (flag_sharp) {
+    *format_ptr = '#';
+    format_ptr++;
+  }
+  if (flag_zero) {
+    *format_ptr = '0';
+    format_ptr++;
+  } /* '0' *must* be the last one */
+
+  if (width != -1) {
+    sprintf(format_ptr, "%i", width);
+    format_ptr += strlen(format_ptr);
+  }
+
+  if (prec != -1) {
+    *format_ptr = '.';
+    format_ptr++;
+    sprintf(format_ptr, "%i", prec);
+    format_ptr += strlen(format_ptr);
+  }
+
+  if (modifier != -1) {
+    *format_ptr = modifier;
+    format_ptr++;
+  }
+
+  *format_ptr = type;
+  format_ptr++;
+  *format_ptr = 0;
+
+  /* vague approximation of minimal length if width or prec are specified */
+  approx_width = width + prec;
+  if (approx_width < 0) /* because width == -1 and/or prec == -1 */
+    approx_width = 0;
+
+  switch (type) {
+    /* int */
+  case 'd':
+  case 'i':
+  case 'o':
+  case 'u':
+  case 'x':
+  case 'X':
+    switch (modifier) {
+    case -1 :
+      return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));
+    case 'l':
+      return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long int));
+    case 'h':
+      return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));
+      /* 'int' instead of 'short int' because default promotion is 'int' */
+    default:
+      INCOHERENT();
+    }
+
+    /* char */
+  case 'c':
+    if (modifier != -1)
+      INCOHERENT();
+    return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));
+    /* 'int' instead of 'char' because default promotion is 'int' */
+
+    /* math */
+  case 'e':
+  case 'f':
+  case 'g':
+  case 'E':
+  case 'G':
+    switch (modifier) {
+    case -1 : /* because of default promotion, no modifier means 'l' */
+    case 'l':
+      return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, double));
+    case 'L':
+      return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long double));
+    default:
+      INCOHERENT();
+    }
+
+    /* string */
+  case 's':
+    return type_s(s, width, prec, format_string, va_arg(s->vargs, const char*));
+
+    /* pointer */
+  case 'p':
+    if (modifier == -1)
+      return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, void *));
+    INCOHERENT();
+
+    /* store */
+  case 'n':
+    if (modifier == -1) {
+      int * p;
+      p = va_arg(s->vargs, int *);
+      if (p != NULL) {
+        *p = s->pseudo_len;
+        return 0;
+      }
+      return EOF;
+    }
+    INCOHERENT();
+
+  } /* switch */
+
+  INCOHERENT();                 /* unknown type */
+
+#undef INCOHERENT
+#undef INCOHERENT_TEST
+#undef SRCTXT
+#undef DESTTXT
+}
+
+/*
+ *  Return value: number of *virtually* written characters
+ *                EOF = error
+ */
+static int core(xprintf_struct *s)
+{
+  size_t len, save_len;
+  char *dummy_base;
+
+  /* basic checks */
+  if ((int)(s->maxlen) <= 0) /* 'int' to check against some conversion */
+    return EOF;           /* error for example if value is (int)-10 */
+  s->maxlen--;      /* because initial maxlen counts final 0 */
+  /* note: now 'maxlen' _can_ be zero */
+
+  if (s->src_string == NULL)
+    s->src_string = "(null)";
+
+  /* struct init and memory allocation */
+  s->buffer_base = NULL;
+  s->buffer_len = 0;
+  s->real_len = 0;
+  s->pseudo_len = 0;
+  if (realloc_buff(s, (size_t)0) == EOF)
+    return EOF;
+  s->dest_string = s->buffer_base;
+
+  /* process source string */
+  for (;;) {
+    /* up to end of source string */
+    if (*(s->src_string) == 0) {
+      *(s->dest_string) = 0;    /* final 0 */
+      len = s->real_len + 1;
+      break;
+    }
+
+    if (dispatch(s) == EOF)
+      goto free_EOF;
+
+    /* up to end of dest string */
+    if (s->real_len >= s->maxlen) {
+      (s->buffer_base)[s->maxlen] = 0; /* final 0 */
+      len = s->maxlen + 1;
+      break;
+    }
+  }
+
+  /* for (v)asnprintf */
+  dummy_base = s->buffer_base;
+  save_len = 0;                 /* just to avoid a compiler warning */
+
+  dummy_base = s->buffer_base + s->real_len;
+  save_len = s->real_len;
+
+  /* process the remaining of source string to compute 'pseudo_len'. We
+   * overwrite again and again, starting at 'dummy_base' because we don't
+   * need the text, only char count. */
+  while(*(s->src_string) != 0) { /* up to end of source string */
+    s->real_len = 0;
+    s->dest_string = dummy_base;
+    if (dispatch(s) == EOF)
+      goto free_EOF;
+  }
+
+  s->buffer_base = (char *)realloc((void *)(s->buffer_base), save_len + 1);
+  if (s->buffer_base == NULL)
+    return EOF; /* should rarely happen because we shrink the buffer */
+  return s->pseudo_len;
+
+  free(s->buffer_base);
+  return s->pseudo_len;
+
+ free_EOF:
+  if (s->buffer_base != NULL)
+    free(s->buffer_base);
+  return EOF;
+}
+
+int vasprintf(char **ptr, const char *format_string, va_list vargs)
+{
+  xprintf_struct s;
+  int retval;
+
+  s.src_string = format_string;
+#ifdef va_copy
+  va_copy (s.vargs, vargs);
+#else
+#ifdef __va_copy
+  __va_copy (s.vargs, vargs);
+#else
+  memcpy (&s.vargs, vargs, sizeof (va_list));
+#endif /* __va_copy */
+#endif /* va_copy */
+  s.maxlen = (size_t)INT_MAX;
+
+  retval = core(&s);
+  va_end(s.vargs);
+  if (retval == EOF) {
+    *ptr = NULL;
+    return EOF;
+  }
+
+  *ptr = s.buffer_base;
+  return retval;
+}