#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) && \
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 *,
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) {
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
if (*ptr != MAGICNO) {
if (swap4(*ptr) != MAGICNO) {
file_error(ms, 0, "bad magic in `%s'");
- goto error;
+ goto error1;
}
needsbyteswap = 1;
} else
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)
(*magicp)++;
if (needsbyteswap)
byteswap(*magicp, *nmagicp);
+ free(dbname);
return RET;
-error:
+error1:
if (fd != -1)
(void)close(fd);
if (mm) {
*magicp = NULL;
*nmagicp = 0;
}
+error2:
+ free(dbname);
return -1;
}
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;
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;
+ }
}
/*
#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;
}
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;
}
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;
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)
{
np = ms->o.pbuf;
op = ms->o.buf;
- eop = op + strlen(ms->o.buf);
+ eop = op + len;
while (op < eop) {
bytesconsumed = mbrtowc(&nextchar, op,
#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
--- /dev/null
+/*
+ * 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;
+}