/*
* vii - buffer and display output
* Copyright (C) 1991-1995, 1999, 2005 Peter Miller
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
* MANIFEST: functions to printf into memory
*/
#include <ac/errno.h>
#include <ac/stdio.h>
#include <ac/stdlib.h>
#include <ac/string.h>
#include <error.h>
#include <mprintf.h>
#include <str.h>
/*
* size to grow memory by
*/
#define QUANTUM 200
/*
* maximum width for numbers
*/
#define MAX_WIDTH (QUANTUM - 1)
/*
* the buffer for storing results
*/
static size_t tmplen;
static size_t length;
static char *tmp;
/*
* NAME
* bigger - grow dynamic memory buffer
*
* SYNOPSIS
* int bigger(void);
*
* DESCRIPTION
* The bigger function is used to grow the dynamic memory buffer
* used by vmprintf to store the formatting results.
* The buffer is increased by QUANTUM bytes.
*
* RETURNS
* int; zero if failed to realloc memory, non-zero if successful.
*
* CAVEATS
* The existing buffer is still valid after failure.
*/
static int bigger _((void));
static int
bigger()
{
char *hold;
size_t nbytes;
nbytes = tmplen + QUANTUM;
errno = 0;
hold = realloc(tmp, nbytes);
if (!hold)
{
if (!errno)
errno = ENOMEM;
return 0;
}
tmplen = nbytes;
tmp = hold;
return 1;
}
/*
* NAME
* build fake - construct formatting specifier string
*
* SYNOPSIS
* void build_fake(char *fake, int flag, int width, int prec, int qual,
* int spec);
*
* DESCRIPTION
* The build_fake function is used to construct a format
* specification string from the arguments presented. This is
* used to guarantee exact replication of sprintf behaviour.
*
* ARGUMENTS
* fake - buffer to store results
* flag - the flag specified (zero if not)
* width - the width specified (zero if not)
* prec - the precision specified (zero if not)
* qual - the qualifier specified (zero if not)
* spec - the formatting specifier specified
*/
static void build_fake _((char *fake, int flag, int width, int precision,
int qualifier, int specifier));
static void
build_fake(fake, flag, width, precision, qualifier, specifier)
char *fake;
int flag;
int width;
int precision;
int qualifier;
int specifier;
{
char *fp;
fp = fake;
*fp++ = '%';
if (flag)
*fp++ = flag;
if (width > 0)
{
sprintf(fp, "%d", width);
fp += strlen(fp);
}
*fp++ = '.';
sprintf(fp, "%d", precision);
fp += strlen(fp);
if (qualifier)
*fp++ = qualifier;
*fp++ = specifier;
*fp = 0;
}
/*
* NAME
* vmprintf_errok - build a formatted string in dynamic memory
*
* SYNOPSIS
* char *vmprintf_errok(char *fmt, va_list ap);
*
* DESCRIPTION
* The vmprintf_errok function is used to build a formatted string
* in memory. It understands all of the ANSI standard sprintf
* formatting directives. Additionally, "%S" may be used to
* manipulate (string_ty *) strings.
*
* ARGUMENTS
* fmt - string specifying formatting to perform
* ap - arguments of types as indicated by the format string
*
* RETURNS
* char *; pointer to buffer containing formatted string
* NULL if there is an error (sets errno)
*
* CAVEATS
* The contents of the buffer pointed to will change between calls
* to vmprintf_errok. The buffer itself may move between calls to
* vmprintf_errok. DO NOT hand the result of vmprintf_errok to
* free().
*/
char *
vmprintf_errok(fmt, ap)
char *fmt;
va_list ap;
{
int width;
int width_set;
int prec;
int prec_set;
int c;
char *s;
int qualifier;
int flag;
char fake[QUANTUM - 1];
/*
* Build the result string in a temporary buffer.
* Grow the temporary buffer as necessary.
*
* It is important to only make one pass across the variable argument
* list. Behaviour is undefined for more than one pass.
*/
if (!tmplen)
{
tmplen = 500;
errno = 0;
tmp = malloc(tmplen);
if (!tmp)
{
if (!errno)
errno = ENOMEM;
return 0;
}
}
length = 0;
s = fmt;
while (*s)
{
c = *s++;
if (c != '%')
{
normal:
if (length >= tmplen && !bigger())
return 0;
tmp[length++] = c;
continue;
}
c = *s++;
/*
* get optional flag
*/
switch (c)
{
case '+':
case '-':
case '#':
case '0':
case ' ':
flag = c;
c = *s++;
break;
default:
flag = 0;
break;
}
/*
* get optional width
*/
width = 0;
width_set = 0;
switch (c)
{
case '*':
width = va_arg(ap, int);
if (width < 0)
{
flag = '-';
width = -width;
}
c = *s++;
width_set = 1;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (;;)
{
width = width * 10 + c - '0';
c = *s++;
switch (c)
{
default:
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
continue;
}
break;
}
width_set = 1;
break;
default:
break;
}
/*
* get optional precision
*/
prec = 0;
prec_set = 0;
if (c == '.')
{
c = *s++;
switch (c)
{
default:
prec_set = 1;
break;
case '*':
c = *s++;
prec = va_arg(ap, int);
if (prec < 0)
{
prec = 0;
break;
}
prec_set = 1;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (;;)
{
prec = prec * 10 + c - '0';
c = *s++;
switch (c)
{
default:
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
continue;
}
break;
}
prec_set = 1;
break;
}
}
/*
* get the optional qualifier
*/
switch (c)
{
default:
qualifier = 0;
break;
case 'l':
case 'h':
case 'L':
qualifier = c;
c = *s++;
break;
}
/*
* get conversion specifier
*/
switch (c)
{
default:
errno = EINVAL;
return 0;
case '%':
goto normal;
case 'c':
{
int a;
char num[MAX_WIDTH + 1];
size_t len;
a = (unsigned char)va_arg(ap, int);
if (!prec_set)
prec = 1;
if (width > MAX_WIDTH)
width = MAX_WIDTH;
if (prec > MAX_WIDTH)
prec = MAX_WIDTH;
build_fake(fake, flag, width, prec, 0, c);
sprintf(num, fake, a);
len = strlen(num);
assert(len < QUANTUM);
if (length + len > tmplen && !bigger())
return 0;
memcpy(tmp + length, num, len);
length += len;
}
break;
case 'd':
case 'i':
{
long a;
char num[MAX_WIDTH + 1];
size_t len;
switch (qualifier)
{
case 'l':
a = va_arg(ap, long);
break;
case 'h':
a = (short)va_arg(ap, int);
break;
default:
a = va_arg(ap, int);
break;
}
if (!prec_set)
prec = 1;
if (width > MAX_WIDTH)
width = MAX_WIDTH;
if (prec > MAX_WIDTH)
prec = MAX_WIDTH;
build_fake(fake, flag, width, prec, 'l', c);
sprintf(num, fake, a);
len = strlen(num);
assert(len < QUANTUM);
if (length + len > tmplen && !bigger())
return 0;
memcpy(tmp + length, num, len);
length += len;
}
break;
case 'e':
case 'f':
case 'g':
case 'E':
case 'F':
case 'G':
{
double a;
char num[MAX_WIDTH + 1];
size_t len;
/*
* Ignore "long double" for now,
* traditional implementations no grok.
*/
a = va_arg(ap, double);
if (!prec_set)
prec = 6;
if (width > MAX_WIDTH)
width = MAX_WIDTH;
if (prec > MAX_WIDTH)
prec = MAX_WIDTH;
build_fake(fake, flag, width, prec, 0, c);
sprintf(num, fake, a);
len = strlen(num);
assert(len < QUANTUM);
if (length + len > tmplen && !bigger())
return 0;
memcpy(tmp + length, num, len);
length += len;
}
break;
case 'n':
switch (qualifier)
{
case 'l':
{
long *a;
a = va_arg(ap, long *);
*a = length;
}
break;
case 'h':
{
short *a;
a = va_arg(ap, short *);
*a = length;
}
break;
default:
{
int *a;
a = va_arg(ap, int *);
*a = length;
}
break;
}
break;
case 'u':
case 'o':
case 'x':
case 'X':
{
unsigned long a;
char num[MAX_WIDTH + 1];
size_t len;
switch (qualifier)
{
case 'l':
a = va_arg(ap, unsigned long);
break;
case 'h':
a = (unsigned short)va_arg(ap, unsigned int);
break;
default:
a = va_arg(ap, unsigned int);
break;
}
if (!prec_set)
prec = 1;
if (prec > MAX_WIDTH)
prec = MAX_WIDTH;
if (width > MAX_WIDTH)
width = MAX_WIDTH;
build_fake(fake, flag, width, prec, 'l', c);
sprintf(num, fake, a);
len = strlen(num);
assert(len < QUANTUM);
if (length + len > tmplen && !bigger())
return 0;
memcpy(tmp + length, num, len);
length += len;
}
break;
case 's':
{
char *a;
size_t len;
a = va_arg(ap, char *);
if (prec_set)
{
char *ep;
ep = (char *)memchr(a, 0, prec);
if (ep)
len = ep - a;
else
len = prec;
}
else
len = strlen(a);
if (!prec_set || len < prec)
prec = len;
if (!width_set || width < prec)
width = prec;
len = width;
while (length + len > tmplen)
{
if (!bigger())
return 0;
}
if (flag != '-')
{
while (width > prec)
{
tmp[length++] = ' ';
width--;
}
}
memcpy(tmp + length, a, prec);
length += prec;
width -= prec;
if (flag == '-')
{
while (width > 0)
{
tmp[length++] = ' ';
width--;
}
}
}
break;
case 'S':
{
string_ty *a;
size_t len;
a = va_arg(ap, string_ty *);
len = a->str_length;
if (!prec_set)
prec = len;
if (len < prec)
prec = len;
if (!width_set)
width = prec;
if (width < prec)
width = prec;
len = width;
while (length + len > tmplen)
{
if (!bigger())
return 0;
}
if (flag != '-')
{
while (width > prec)
{
tmp[length++] = ' ';
width--;
}
}
memcpy(tmp + length, a->str_text, prec);
length += prec;
width -= prec;
if (flag == '-')
{
while (width > 0)
{
tmp[length++] = ' ';
width--;
}
}
}
break;
}
}
/*
* append a trailing NUL
*/
if (length >= tmplen && !bigger())
return 0;
tmp[length] = 0;
/*
* return the temporary string
*/
return tmp;
}
/*
* NAME
* mprintf_errok - build a formatted string in dynamic memory
*
* SYNOPSIS
* char *mprintf_errok(char *fmt, ...);
*
* DESCRIPTION
* The mprintf_errok function is used to build a formatted string
* in memory. It understands all of the ANSI standard sprintf
* formatting directives. Additionally, "%S" may be used to
* manipulate (string_ty *) strings.
*
* ARGUMENTS
* fmt - string spefiifying formatting to perform
* ... - arguments of types as indicated by the format string
*
* RETURNS
* char *; pointer to buffer containing formatted string
* NULL if there is an error (sets errno)
*
* CAVEATS
* The contents of the buffer pointed to will change between calls
* to mprintf_errok. The buffer itself may move between calls to
* mprintf_errok. DO NOT hand the result of mprintf_errok to
* free().
*/
char *
mprintf_errok(fmt sva_last)
char *fmt;
sva_last_decl
{
char *result;
va_list ap;
sva_init(ap, fmt);
result = vmprintf_errok(fmt, ap);
va_end(ap);
return result;
}
/*
* NAME
* vmprintf - build a formatted string in dynamic memory
*
* SYNOPSIS
* char *vmprintf(char *fmt, va_list ap);
*
* DESCRIPTION
* The vmprintf function is used to build a formatted string in memory.
* It understands all of the ANSI standard sprintf formatting directives.
* Additionally, "%S" may be used to manipulate (string_ty *) strings.
*
* ARGUMENTS
* fmt - string spefiifying formatting to perform
* ap - arguments of types as indicated by the format string
*
* RETURNS
* char *; pointer to buffer containing formatted string
*
* CAVEATS
* On error, prints a fatal error message and exists; does not return.
*
* The contents of the buffer pointed to will change between calls
* to vmprintf. The buffer itself may move between calls to vmprintf.
* DO NOT hand the result of vmprintf to free().
*/
char *
vmprintf(fmt, ap)
char *fmt;
va_list ap;
{
char *result;
result = vmprintf(fmt, ap);
if (!result)
nfatal("mprintf \"%s\"", fmt);
return result;
}
/*
* NAME
* mprintf - build a formatted string in dynamic memory
*
* SYNOPSIS
* char *mprintf(char *fmt, ...);
*
* DESCRIPTION
* The mprintf function is used to build a formatted string in memory.
* It understands all of the ANSI standard sprintf formatting directives.
* Additionally, "%S" may be used to manipulate (string_ty *) strings.
*
* ARGUMENTS
* fmt - string spefiifying formatting to perform
* ... - arguments of types as indicated by the format string
*
* RETURNS
* char *; pointer to buffer containing formatted string
*
* CAVEATS
* On error, prints a fatal error message and exists; does not return.
*
* The contents of the buffer pointed to will change between calls
* to mprintf. The buffer itself may move between calls to mprintf.
* DO NOT hand the result of mprintfe to free().
*/
char *
mprintf(fmt sva_last)
char *fmt;
sva_last_decl
{
char *result;
va_list ap;
sva_init(ap, fmt);
result = vmprintf(fmt, ap);
va_end(ap);
return result;
}
/*
* NAME
* vmprintf_str - build a formatted string in dynamic memory
*
* SYNOPSIS
* char *vmprintf_str(char *fmt, va_list ap);
*
* DESCRIPTION
* The vmprintf_str function is used to build a formatted string in memory.
* It understands all of the ANSI standard sprintf formatting directives.
* Additionally, "%S" may be used to manipulate (string_ty *) strings.
*
* ARGUMENTS
* fmt - string spefiifying formatting to perform
* ap - arguments of types as indicated by the format string
*
* RETURNS
* string_ty *; string containing formatted string
*
* CAVEATS
* On error, prints a fatal error message and exists; does not return.
*
* It is the resposnsibility of the caller to invoke str_free to release
* the results when finished with.
*/
string_ty *
vmprintf_str(fmt, ap)
char *fmt;
va_list ap;
{
if (!vmprintf_errok(fmt, ap))
nfatal("mprintf \"%s\"", fmt);
return str_n_from_c(tmp, length);
}
syntax highlighted by Code2HTML, v. 0.9.1