Commit 827dbcb1 authored by Richard Levitte's avatar Richard Levitte
Browse files

Change the printing mahine used by BIO_printf() and friends so it can

handle an externally provided "static" buffer as well a a dynamic
buffer.  The "static" buffer is filled first, but if overflowed, the
dynamic buffer is used instead, being allocated somewhere i the heap.

This combines the benefits of putting the output in a preallocated
buffer (on the stack, for example) and in a buffer that grows
somewhere in the heap.
parent 47770c4d
Loading
Loading
Loading
Loading
+89 −160
Original line number Diff line number Diff line
@@ -113,24 +113,15 @@
#define LLONG long
#endif

static void fmtstr     (void (*)(char **, size_t *, size_t *, int),
			char **, size_t *, size_t *, const char *, int, int,
			int);
static void fmtint     (void (*)(char **, size_t *, size_t *, int),
			char **, size_t *, size_t *, LLONG, int, int, int, int);
static void fmtfp      (void (*)(char **, size_t *, size_t *, int),
			char **, size_t *, size_t *, LDOUBLE, int, int, int);
static int dopr_isbig (size_t, size_t);
static int dopr_copy (size_t);
static void dopr_outch (char **, size_t *, size_t *, int);
#ifdef USE_ALLOCATING_PRINT
static int doapr_isbig (size_t, size_t);
static int doapr_copy (size_t);
static void doapr_outch (char **, size_t *, size_t *, int);
#endif
static void _dopr(void (*)(char **, size_t *, size_t *, int),
		  int (*)(size_t, size_t), int (*)(size_t),
		  char **buffer, size_t *maxlen, size_t *retlen, int *truncated,
static void fmtstr     (char **, char **, size_t *, size_t *,
			const char *, int, int, int);
static void fmtint     (char **, char **, size_t *, size_t *,
			LLONG, int, int, int, int);
static void fmtfp      (char **, char **, size_t *, size_t *,
			LDOUBLE, int, int, int);
static void doapr_outch (char **, char **, size_t *, size_t *, int);
static void _dopr(char **sbuffer, char **buffer,
		  size_t *maxlen, size_t *retlen, int *truncated,
		  const char *format, va_list args);

/* format read states */
@@ -162,40 +153,9 @@ static void _dopr(void (*)(char **, size_t *, size_t *, int),
#define char_to_int(p) (p - '0')
#define MAX(p,q) ((p >= q) ? p : q)

#ifndef USE_ALLOCATING_PRINT
static void
dopr(
    char *buffer,
    size_t maxlen,
    size_t *retlen,
    const char *format,
    va_list args)
{
    int ignored;
    _dopr(dopr_outch, dopr_isbig, dopr_copy,
        &buffer, &maxlen, retlen, &ignored, format, args);
}

#else
static void
doapr(
    char **buffer,
    size_t *retlen,
    const char *format,
    va_list args)
{
    size_t dummy_maxlen = 0;
    int ignored;
    _dopr(doapr_outch, doapr_isbig, doapr_copy,
        buffer, &dummy_maxlen, retlen, &ignored, format, args);
}
#endif

static void
_dopr(
    void (*outch_fn)(char **, size_t *, size_t *, int),
    int (*isbig_fn)(size_t, size_t),
    int (*copy_fn)(size_t),
    char **sbuffer,
    char **buffer,
    size_t *maxlen,
    size_t *retlen,
@@ -220,7 +180,7 @@ _dopr(
    ch = *format++;

    while (state != DP_S_DONE) {
        if ((ch == '\0') || (*isbig_fn)(currlen, *maxlen))
        if (ch == '\0' || (buffer == NULL && currlen >= *maxlen))
            state = DP_S_DONE;

        switch (state) {
@@ -228,7 +188,7 @@ _dopr(
            if (ch == '%')
                state = DP_S_FLAGS;
            else
                (*outch_fn)(buffer, &currlen, maxlen, ch);
                doapr_outch(sbuffer,buffer, &currlen, maxlen, ch);
            ch = *format++;
            break;
        case DP_S_FLAGS:
@@ -334,7 +294,7 @@ _dopr(
                    value = va_arg(args, int);
                    break;
                }
                fmtint(outch_fn, buffer, &currlen, maxlen,
                fmtint(sbuffer, buffer, &currlen, maxlen,
                       value, 10, min, max, flags);
                break;
            case 'X':
@@ -360,7 +320,7 @@ _dopr(
                        unsigned int);
                    break;
                }
                fmtint(outch_fn, buffer, &currlen, maxlen, value,
                fmtint(sbuffer, buffer, &currlen, maxlen, value,
                       ch == 'o' ? 8 : (ch == 'u' ? 10 : 16),
                       min, max, flags);
                break;
@@ -369,7 +329,7 @@ _dopr(
                    fvalue = va_arg(args, LDOUBLE);
                else
                    fvalue = va_arg(args, double);
                fmtfp(outch_fn, buffer, &currlen, maxlen,
                fmtfp(sbuffer, buffer, &currlen, maxlen,
                      fvalue, min, max, flags);
                break;
            case 'E':
@@ -389,19 +349,23 @@ _dopr(
                    fvalue = va_arg(args, double);
                break;
            case 'c':
                (*outch_fn)(buffer, &currlen, maxlen,
                doapr_outch(sbuffer, buffer, &currlen, maxlen,
                    va_arg(args, int));
                break;
            case 's':
                strvalue = va_arg(args, char *);
                if (max < 0)
                    max = (*copy_fn)(*maxlen);
                fmtstr(outch_fn, buffer, &currlen, maxlen, strvalue,
                if (max < 0) {
		    if (buffer)
			max = INT_MAX;
		    else
			max = *maxlen;
		}
                fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue,
                       flags, min, max);
                break;
            case 'p':
                value = (long)va_arg(args, void *);
                fmtint(outch_fn, buffer, &currlen, maxlen,
                fmtint(sbuffer, buffer, &currlen, maxlen,
                    value, 16, min, max, flags);
                break;
            case 'n': /* XXX */
@@ -424,7 +388,7 @@ _dopr(
                }
                break;
            case '%':
                (*outch_fn)(buffer, &currlen, maxlen, ch);
                doapr_outch(sbuffer, buffer, &currlen, maxlen, ch);
                break;
            case 'w':
                /* not supported yet, treat as next char */
@@ -448,14 +412,14 @@ _dopr(
    *truncated = (currlen > *maxlen - 1);
    if (*truncated)
        currlen = *maxlen - 1;
    (*buffer)[currlen] = '\0';
    *retlen = currlen;
    doapr_outch(sbuffer, buffer, &currlen, maxlen, '\0');
    *retlen = currlen - 1;
    return;
}

static void
fmtstr(
    void (*outch_fn)(char **, size_t *, size_t *, int),
    char **sbuffer,
    char **buffer,
    size_t *currlen,
    size_t *maxlen,
@@ -478,16 +442,16 @@ fmtstr(
        padlen = -padlen;

    while ((padlen > 0) && (cnt < max)) {
        (*outch_fn)(buffer, currlen, maxlen, ' ');
        doapr_outch(sbuffer, buffer, currlen, maxlen, ' ');
        --padlen;
        ++cnt;
    }
    while (*value && (cnt < max)) {
        (*outch_fn)(buffer, currlen, maxlen, *value++);
        doapr_outch(sbuffer, buffer, currlen, maxlen, *value++);
        ++cnt;
    }
    while ((padlen < 0) && (cnt < max)) {
        (*outch_fn)(buffer, currlen, maxlen, ' ');
        doapr_outch(sbuffer, buffer, currlen, maxlen, ' ');
        ++padlen;
        ++cnt;
    }
@@ -495,7 +459,7 @@ fmtstr(

static void
fmtint(
    void (*outch_fn)(char **, size_t *, size_t *, int),
    char **sbuffer,
    char **buffer,
    size_t *currlen,
    size_t *maxlen,
@@ -552,28 +516,28 @@ fmtint(

    /* spaces */
    while (spadlen > 0) {
        (*outch_fn)(buffer, currlen, maxlen, ' ');
        doapr_outch(sbuffer, buffer, currlen, maxlen, ' ');
        --spadlen;
    }

    /* sign */
    if (signvalue)
        (*outch_fn)(buffer, currlen, maxlen, signvalue);
        doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue);

    /* zeros */
    if (zpadlen > 0) {
        while (zpadlen > 0) {
            (*outch_fn)(buffer, currlen, maxlen, '0');
            doapr_outch(sbuffer, buffer, currlen, maxlen, '0');
            --zpadlen;
        }
    }
    /* digits */
    while (place > 0)
        (*outch_fn)(buffer, currlen, maxlen, convert[--place]);
        doapr_outch(sbuffer, buffer, currlen, maxlen, convert[--place]);

    /* left justified spaces */
    while (spadlen < 0) {
        (*outch_fn)(buffer, currlen, maxlen, ' ');
        doapr_outch(sbuffer, buffer, currlen, maxlen, ' ');
        ++spadlen;
    }
    return;
@@ -612,7 +576,7 @@ round(LDOUBLE value)

static void
fmtfp(
    void (*outch_fn)(char **, size_t *, size_t *, int),
    char **sbuffer,
    char **buffer,
    size_t *currlen,
    size_t *maxlen,
@@ -693,117 +657,85 @@ fmtfp(

    if ((flags & DP_F_ZERO) && (padlen > 0)) {
        if (signvalue) {
            (*outch_fn)(buffer, currlen, maxlen, signvalue);
            doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue);
            --padlen;
            signvalue = 0;
        }
        while (padlen > 0) {
            (*outch_fn)(buffer, currlen, maxlen, '0');
            doapr_outch(sbuffer, buffer, currlen, maxlen, '0');
            --padlen;
        }
    }
    while (padlen > 0) {
        (*outch_fn)(buffer, currlen, maxlen, ' ');
        doapr_outch(sbuffer, buffer, currlen, maxlen, ' ');
        --padlen;
    }
    if (signvalue)
        (*outch_fn)(buffer, currlen, maxlen, signvalue);
        doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue);

    while (iplace > 0)
        (*outch_fn)(buffer, currlen, maxlen, iconvert[--iplace]);
        doapr_outch(sbuffer, buffer, currlen, maxlen, iconvert[--iplace]);

    /*
     * Decimal point. This should probably use locale to find the correct
     * char to print out.
     */
    if (max > 0) {
        (*outch_fn)(buffer, currlen, maxlen, '.');
        doapr_outch(sbuffer, buffer, currlen, maxlen, '.');

        while (fplace > 0)
            (*outch_fn)(buffer, currlen, maxlen, fconvert[--fplace]);
            doapr_outch(sbuffer, buffer, currlen, maxlen, fconvert[--fplace]);
    }
    while (zpadlen > 0) {
        (*outch_fn)(buffer, currlen, maxlen, '0');
        doapr_outch(sbuffer, buffer, currlen, maxlen, '0');
        --zpadlen;
    }

    while (padlen < 0) {
        (*outch_fn)(buffer, currlen, maxlen, ' ');
        doapr_outch(sbuffer, buffer, currlen, maxlen, ' ');
        ++padlen;
    }
}

static int
dopr_copy(
    size_t len)
{
    return len;
}

#ifdef USE_ALLOCATING_PRINT
static int
doapr_copy(
    size_t len)
{
    /* Return as high an integer as possible */
    return INT_MAX;
}
#endif

static int
dopr_isbig(
    size_t currlen,
    size_t maxlen)
{
    return currlen > maxlen;
}

#ifdef USE_ALLOCATING_PRINT
static int
doapr_isbig(
    size_t currlen,
    size_t maxlen)
{
    return 0;
}
#endif

static void
dopr_outch(
    char **buffer,
    size_t *currlen,
    size_t *maxlen,
    int c)
{
    if (*currlen < *maxlen)
        (*buffer)[(*currlen)++] = (char)c;
    return;
}

#ifdef USE_ALLOCATING_PRINT
static void
doapr_outch(
    char **sbuffer,
    char **buffer,
    size_t *currlen,
    size_t *maxlen,
    int c)
{
    /* If we haven't at least one buffer, someone has doe a big booboo */
    assert(*sbuffer != NULL || buffer != NULL);

    if (buffer) {
	while (*currlen >= *maxlen) {
	    if (*buffer == NULL) {
		assert(*sbuffer != NULL);
		if (*maxlen == 0)
		    *maxlen = 1024;
		*buffer = OPENSSL_malloc(*maxlen);
    }
    while (*currlen >= *maxlen) {
		if (*currlen > 0)
		    memcpy(*buffer, *sbuffer, *currlen);
		*sbuffer = NULL;
	    } else {
		*maxlen += 1024;
		*buffer = OPENSSL_realloc(*buffer, *maxlen);
	    }
	}
	/* What to do if *buffer is NULL? */
    assert(*buffer != NULL);
	assert(*sbuffer != NULL || *buffer != NULL);
    }

    if (*currlen < *maxlen) {
	if (*sbuffer)
	    (*sbuffer)[(*currlen)++] = (char)c;
	else
	    (*buffer)[(*currlen)++] = (char)c;
    }

    return;
}
#endif

/***************************************************************************/

@@ -824,29 +756,26 @@ int BIO_vprintf (BIO *bio, const char *format, va_list args)
	{
	int ret;
	size_t retlen;
#ifdef USE_ALLOCATING_PRINT
	char *hugebuf;
#else
	MS_STATIC char hugebuf[1024*10];
#endif
	char *hugebufp = hugebuf;
	size_t hugebufsize = sizeof(hugebuf);
	char *dynbuf = NULL;
	int ignored;

#ifndef USE_ALLOCATING_PRINT
	hugebuf[0]='\0';
	dopr(hugebuf, sizeof(hugebuf), &retlen, format, args);
#else
	hugebuf = NULL;
	dynbuf = NULL;
	CRYPTO_push_info("doapr()");
	doapr(&hugebuf, &retlen, format, args);
	if (hugebuf)
	_dopr(&hugebufp, &dynbuf, &hugebufsize,
		&retlen, &ignored, format, args);
	if (dynbuf)
		{
		ret=BIO_write(bio, dynbuf, (int)retlen);
		OPENSSL_free(dynbuf);
		}
	else
		{
#endif
		ret=BIO_write(bio, hugebuf, (int)retlen);

#ifdef USE_ALLOCATING_PRINT
		OPENSSL_free(hugebuf);
		}
	CRYPTO_pop_info();
#endif
	return(ret);
	}

@@ -872,8 +801,8 @@ int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args)
	size_t retlen;
	int truncated;

	_dopr(dopr_outch, dopr_isbig, dopr_copy,
		&buf, &n, &retlen, &truncated, format, args);
	_dopr(&buf, NULL, &n, &retlen, &truncated, format, args);

	if (truncated)
		/* In case of truncation, return -1 like traditional snprintf.
		 * (Current drafts for ISO/IEC 9899 say snprintf should return