/* $Id: compat.c 1555 2006-08-17 20:10:28Z mipsator $ */
/*
* Copyright (c) 2003-2005 Damien Couderc
* All rights reserved.
*
* 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.
* - Neither the name of the copyright holder(s) nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
* COPYRIGHT HOLDERS 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 <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <math.h>
#include <stdlib.h>
#include "compat/config.h"
#ifdef HAVE_INTMAX_T
#include <stdint.h>
#endif
#if defined(HAVE_WCHAR_T) || defined (HAVE_WINT_T)
#include <wchar.h>
#endif
#ifdef HAVE_PTRDIFF_T
#include <stddef.h>
#endif
#include "compat.h"
#include "compat/pmk_ctype.h"
#include "compat/pmk_libgen.h"
#include "compat/pmk_stdbool.h"
#include "compat/pmk_stdio.h"
#include "compat/pmk_string.h"
#include "compat/pmk_sys_types.h"
#include "compat/pmk_unistd.h"
/**********
* macros *
***********************************************************************/
/* macros for structure */
#define SET_FLAG(f) data.flags = data.flags | f
#define UNSET_FLAG(f) data.flags = data.flags & (FLAG_ALL ^ f);
#define F_IS_SET(f) (data.flags & f) != 0
#define F_IS_UNSET(f) (data.flags & f) == 0
/* macros for pointer of structure */
#define SET_PFLAG(f) pdt->flags = pdt->flags | f
#define UNSET_PFLAG(f) pdt->flags = pdt->flags & (FLAG_ALL ^ f);
#define PF_IS_SET(f) (pdt->flags & f) != 0
#define PF_IS_UNSET(f) (pdt->flags & f) == 0
/*#define DEBUG_VSNPRINTF 1*/
/*************
* functions *
***********************************************************************/
/*****************
* fill_buffer() *
***********************************************************************
DESCR
Append a character in the buffer if it does not override the
buffer's length
IN
pdt : vsnprintf common data structure
chr : char to append
OUT
NONE
***********************************************************************/
static void fill_buffer(vsnp_t *pdt, char chr) {
#ifdef DEBUG_VSNPRINTF
printf("cursor = %d, char = '%c'\n", pdt->cur, chr);
#endif
/* if buffer is not full */
if (pdt->cur < pdt->len) {
/* insert character */
pdt->buf[pdt->cur] = chr;
}
/* increment cursor position */
pdt->cur++;
}
/******************
* build_number() *
***********************************************************************
DESCR
Convert an unsigned integer into a string. The resulting string is
in the reverse order without terminating null character.
IN
buffer : buffer location
len : buffer size
value : unsigned integer to convert
pdt : vsnprintf common data structure
OUT
size of the resulting string
***********************************************************************/
static size_t build_number(char *buf, size_t len, unsigned_t value,
base_t base, flag_t flags) {
char *basestr;
size_t vlen = 0;
/* if value is equal to zero */
if (value == 0) {
buf[0] = '0';
return(1);
}
/* select reference string relative to the case */
if ((flags & FLAG_UPPERCASE) != 0) {
basestr = UPPER_BASE;
} else {
basestr = LOWER_BASE;
}
/* create number string in reverse order */
while ((value > 0) && (vlen < len)) {
buf[vlen] = basestr[value % base];
value = value / base;
vlen++;
}
return(vlen);
}
/*****************
* convert_int() *
***********************************************************************
DESCR
Convert an integer value
IN
pdt : vsnprintf common data structure
value : unsigned integer to convert
OUT
NONE
***********************************************************************/
static void convert_int(vsnp_t *pdt, unsigned_t value) {
char nbuf[MAXINTLEN+1],
sign = ' ';
int vlen,
zplen,
splen,
have_sign = 0,
have_hex_prefix = 0;
if (PF_IS_SET(FLAG_NEGATIVE_VALUE)) {
/* on negative value set the sign */
have_sign = 1;
sign = '-';
} else {
/* else if + flag is set display the positive sign */
if (PF_IS_SET(FLAG_SIGNED)) {
have_sign = 1;
sign = '+';
}
}
vlen = build_number(nbuf, sizeof(nbuf), value, pdt->base, pdt->flags);
/* compute zero padding */
zplen = pdt->prec - vlen;
if (zplen < 0) {
zplen = 0;
}
/* compute space padding */
if (pdt->prec > vlen) {
splen = pdt->fwidth - pdt->prec;
} else {
splen = pdt->fwidth - vlen;
}
if (have_sign != 0) {
splen--;
}
if (PF_IS_SET(FLAG_ALTERNATIVE_FORM)) {
switch (pdt->base) {
case BASE_OCT :
/* octal alternative form */
if ((value == 0) && (pdt->prec == 0)) {
pdt->prec = 1;
}
break;
case BASE_HEX :
/* hex alternative form */
if (value > 0) {
have_hex_prefix = 1;
splen = splen - 2; /* length of '0x' */
}
break;
}
}
if (splen < 0) {
splen = 0;
}
/* right justify space padding */
if (PF_IS_UNSET(FLAG_LEFT_JUSTIFIED)) {
while (splen > 0) {
fill_buffer(pdt, ' ');
splen--;
}
}
/* if we have a sign */
if (have_sign != 0) {
fill_buffer(pdt, sign);
}
/* hex alternative form */
if (have_hex_prefix != 0) {
fill_buffer(pdt, '0');
if (PF_IS_UNSET(FLAG_UPPERCASE)) {
fill_buffer(pdt, 'x');
} else {
fill_buffer(pdt, 'X');
}
}
/* zero padding */
while (zplen > 0) {
fill_buffer(pdt, '0');
zplen--;
}
/* converted integer */
while (vlen > 0) {
fill_buffer(pdt, nbuf[vlen - 1]);
vlen--;
}
/* left justify space padding */
if (PF_IS_SET(FLAG_LEFT_JUSTIFIED)) {
while (splen > 0) {
fill_buffer(pdt, ' ');
splen--;
}
}
}
/******************
* convert_sint() *
***********************************************************************
DESCR
Convert a signed integer
IN
pdt : vsnprintf common data structure
value : unsigned integer to convert
OUT
NONE
***********************************************************************/
static void convert_sint(vsnp_t *pdt, signed_t value) {
/* check if value is negative */
if (value < 0) {
/* if true set flag */
SET_PFLAG(FLAG_NEGATIVE_VALUE);
value = -value;
}
/* call generic conversion procedure */
convert_int(pdt, (unsigned_t) value);
}
/*****************
* convert_str() *
***********************************************************************
DESCR
Convert the string. It means copying the string in the place of the
conversion identifier.
IN
pdt : vsnprintf common data structure
ptr : string to convert
OUT
NONE
***********************************************************************/
static void convert_str(vsnp_t *pdt, char *ptr) {
while ((pdt->cur < pdt->len) && (pdt->prec > 0) && (*ptr != '\0')) {
fill_buffer(pdt, *ptr);
ptr++;
pdt->prec--;
}
}
/*****************
* conv_to_exp() *
***********************************************************************
DESCR
Convert to exponent form
IN
pdt : vsnprintf common data structure
expnt : exponent value
OUT
exponent presence flag
***********************************************************************/
static int conv_to_exp(vsnp_t *pdt, flt_t *pval, int *pexp) {
flt_t uval;
int expnt,
have_exp = 1;
#ifdef DEBUG_VSNPRINTF
printf("conv_to_exp() -> in\n");
#endif
/* work on local variable to save value in case of g conversion */
uval = *pval;
/* init exponent value */
expnt = 0;
/* process value to get only one digit before decimal point */
if (uval >= 0) {
while (uval > pdt->base) {
uval = uval / pdt->base;
expnt++;
}
} else {
while (uval < 0) {
uval = uval * pdt->base;
expnt--;
}
}
/* check if exponent is needed for g conversion */
if (PF_IS_SET(FLAG_G_CONVERSION)) {
if ((expnt >= -4) && (expnt < pdt->prec)) { /* XXX use define for ISOC99 lower limit */
/* unset exponent flag */
have_exp = 0;
/* get back to original value */
uval = *pval;
}
}
/* set values */
*pval = uval;
*pexp = expnt;
#ifdef DEBUG_VSNPRINTF
printf("conv_to_exp() -> out\n");
#endif
return(have_exp);
}
/***************
* print_exp() *
***********************************************************************
DESCR
process exponent
IN
pdt : vsnprintf common data structure
expnt : exponent value
OUT
NONE
***********************************************************************/
static void print_exp(vsnp_t *pdt, int expnt) {
char ebuf[MAXINTLEN+1],
sign= ' ';
#ifdef DEBUG_VSNPRINTF
printf("print_exp() -> in\n");
#endif
/* exponent sign */
if (expnt < 0) {
sign = '-';
} else {
sign = '+';
}
/* exponent type may vary */
ebuf[1] = '0'; /* init */
switch (pdt->base) {
case BASE_DEC :
/* g or G conversion */
if (PF_IS_SET(FLAG_UPPERCASE)) {
fill_buffer(pdt, 'E');
} else {
fill_buffer(pdt, 'e');
}
fill_buffer(pdt, sign);
/* exponent 2 digits */
build_number(ebuf, 2, expnt, BASE_DEC, FLAG_NONE);
fill_buffer(pdt, ebuf[1]);
fill_buffer(pdt, ebuf[0]);
break;
case BASE_HEX :
/* a or A conversion */
if (PF_IS_SET(FLAG_UPPERCASE)) {
fill_buffer(pdt, 'P');
} else {
fill_buffer(pdt, 'p');
}
fill_buffer(pdt, sign);
/* exponent 1 digit */
build_number(ebuf, 1, expnt, BASE_DEC, FLAG_NONE);
fill_buffer(pdt, ebuf[0]);
break;
}
#ifdef DEBUG_VSNPRINTF
printf("print_exp() -> out\n");
#endif
}
/************************
* convert_real_float() *
***********************************************************************
DESCR
Convert a floating point number
IN
pdt : vsnprintf common data structure
value : unsigned integer to convert
OUT
NONE
TODO
-inf
-NaN
***********************************************************************/
static void convert_real_float(vsnp_t *pdt, flt_t value) {
char ibuf[MAXINTLEN+1],
fbuf[MAXINTLEN+1],
sign= ' ';
flt_t frac,
uval;
int expnt = 0,
explen = 0,
fplen,
fzplen,
have_alt_form,
have_dot = 0,
have_exp = 0,
have_g_conv,
have_sign = 0,
izplen,
splen,
skip_zero = 0,
t;
long intval,
fracval,
lt;
size_t ilen,
flen;
#ifdef DEBUG_VSNPRINTF
printf("convert_real_float() -> in\n");
#endif
have_g_conv = (int) (pdt->flags & FLAG_G_CONVERSION);
have_alt_form = (int) (pdt->flags & FLAG_ALTERNATIVE_FORM);
/* for g conversion without alternative form */
if ((have_g_conv != 0) && (have_alt_form == 0)) {
/* we skip ending zeros */
skip_zero = 1;
}
if (value < 0) {
/* on negative value set the sign */
have_sign = 1;
sign = '-';
uval = -value;
} else {
/* else if + flag is set display the positive sign */
if (PF_IS_SET(FLAG_SIGNED)) {
have_sign = 1;
sign = '+';
}
uval = value;
}
if ((PF_IS_SET(FLAG_EXPONENT)) || (have_g_conv != 0)) {
/* process eventual exponent */
have_exp = conv_to_exp(pdt, &uval, &expnt);
}
/* get integral part of the value */
intval = (long) uval;
/* build integral part number */
ilen = build_number(ibuf, sizeof(ibuf), intval, pdt->base, pdt->flags);
if (have_g_conv != 0) {
pdt->prec = pdt->prec - ilen;
if (pdt->prec < 0) {
pdt->prec = 0;
}
}
if (pdt->prec > 0) {
/* get the fractional part */
frac = uval - (flt_t) intval;
/* shift as digits as precision specify */
t = pdt->prec;
/* init round limit */
lt = 1;
/* multiply successively by base to obtain the desired precision */
while (t > 0) {
frac = frac * pdt->base;
lt = lt * pdt->base;
t--;
}
/* get integral part of the result */
fracval = (long) frac;
/* rount value if needed */
if ((frac - fracval) > 0.5) {
fracval++;
/* if the rounded value add a digit (eg 999 -> 1000) */
if (fracval == lt) {
/* then adjust fractional value */
fracval = fracval / pdt->base;
}
}
/* unset signed flag for fractional part */
UNSET_PFLAG(FLAG_SIGNED);
/* build fractional part number */
flen = build_number(fbuf, sizeof(fbuf), fracval, pdt->base, pdt->flags);
} else {
flen = 0;
fracval = 0;
}
/* need a dot ? */
if ((have_alt_form != 0) || (pdt->prec > 0)) {
have_dot = 1;
}
/* specific treatment for g conversion */
if ((skip_zero != 0) && (fracval == 0)) {
have_dot = 0;
flen = 0;
pdt->prec = 0;
}
/* fractal part len */
if (flen < (size_t) pdt->prec) {
fplen = pdt->prec;
fzplen = pdt->prec - flen;
} else {
fplen = flen;
fzplen = 0;
}
/* exponent len */
if (have_exp != 0) {
switch (pdt->base) {
case BASE_DEC :
explen = 4;
break;
case BASE_HEX :
explen = 3;
break;
}
} else {
explen = 0;
}
/* total string len */
t = ilen + fplen + explen;
/* sign */
if (have_sign != 0) {
t++;
}
/* decimal point */
if (have_dot != 0) {
t++;
}
/* a or A conversion => hex prefix */
if (pdt->base == BASE_HEX) {
t= t + 2;
}
/* compute space or zero (integer part) padding */
if (PF_IS_SET(FLAG_ZERO_PADDING)) {
izplen = pdt->fwidth - t;
if (izplen < 0) {
izplen = 0;
}
splen = 0;
} else {
splen = pdt->fwidth - t;
if (splen < 0) {
splen = 0;
}
izplen = 0;
}
/* right justify space padding */
if (PF_IS_UNSET(FLAG_LEFT_JUSTIFIED)) {
while (splen > 0) {
fill_buffer(pdt, ' ');
splen--;
}
}
/* if we have a sign */
if (have_sign != 0) {
fill_buffer(pdt, sign);
}
/* hex alternative form */
if (pdt->base == BASE_HEX) {
fill_buffer(pdt, '0');
if (PF_IS_UNSET(FLAG_UPPERCASE)) {
fill_buffer(pdt, 'x');
} else {
fill_buffer(pdt, 'X');
}
}
/* zero padding */
while (izplen > 0) {
fill_buffer(pdt, '0');
izplen--;
}
/* integral part */
while (ilen > 0) {
fill_buffer(pdt, ibuf[ilen - 1]);
ilen--;
}
if (have_dot) {
fill_buffer(pdt, '.');
if (pdt->prec > 0) {
/* fractional part */
while (flen > 0) {
fill_buffer(pdt, fbuf[flen - 1]);
flen--;
}
if (skip_zero == 0) {
/* zero padding */
while (fzplen > 0) {
fill_buffer(pdt, '0');
fzplen--;
}
}
}
}
if (have_exp != 0) {
/* exponent part */
print_exp(pdt, expnt);
}
/* left justify space padding */
if (PF_IS_SET(FLAG_LEFT_JUSTIFIED)) {
while (splen > 0) {
fill_buffer(pdt, ' ');
splen--;
}
}
#ifdef DEBUG_VSNPRINTF
printf("convert_real_float() -> out\n");
#endif
}
/***************************
* convert_special_float() *
***********************************************************************
DESCR
Convert a NaN or an infinite float
IN
pdt : vsnprintf common data structure
value : unsigned integer to convert
type : type of special float
OUT
NONE
***********************************************************************/
static void convert_special_float(vsnp_t *pdt, flt_t value, int type) {
char *fstr = NULL,
sign = ' ';
int have_sign = 0;
size_t i,
len,
splen;
#ifdef DEBUG_VSNPRINTF
printf("convert_special_float() -> in\n");
#endif
if (value < 0) {
/* on negative value set the sign */
have_sign = 1;
sign = '-';
} else {
/* else if + flag is set display the positive sign */
if (PF_IS_SET(FLAG_SIGNED)) {
have_sign = 1;
sign = '+';
}
}
/* set special float type string */
switch (type) {
case FLT_IS_NAN :
/* float is NaN */
if (PF_IS_SET(FLAG_UPPERCASE)) {
fstr = UPPER_NAN;
} else {
fstr = LOWER_NAN;
}
break;
case FLT_IS_INF :
/* float is infinite */
if (PF_IS_UNSET(FLAG_UPPERCASE)) {
fstr = UPPER_INF;
} else {
fstr = LOWER_INF;
}
break;
}
/* compute space padding length */
len = strlen(fstr);
splen = pdt->fwidth - len;
if (have_sign != 0) {
splen--;
}
/* right justify space padding */
if (PF_IS_UNSET(FLAG_LEFT_JUSTIFIED)) {
while (splen > 0) {
fill_buffer(pdt, ' ');
splen--;
}
}
/* if we have a sign */
if (have_sign != 0) {
fill_buffer(pdt, sign);
}
/* special float string */
for (i = 0 ; i < len ; i++) {
fill_buffer(pdt, fstr[i]);
}
/* left justify space padding */
if (PF_IS_SET(FLAG_LEFT_JUSTIFIED)) {
while (splen > 0) {
fill_buffer(pdt, ' ');
splen--;
}
}
#ifdef DEBUG_VSNPRINTF
printf("convert_special_float() -> out\n");
#endif
}
/*******************
* convert_float() *
***********************************************************************
DESCR
Wrapper of floating point conversion
IN
pdt : vsnprintf common data structure
value : unsigned integer to convert
OUT
NONE
***********************************************************************/
static void convert_float(vsnp_t *pdt, flt_t value) {
#ifdef DEBUG_VSNPRINTF
printf("convert_float() -> in\n");
#endif
if (isnan(value) != 0) {
/* float is NaN */
convert_special_float(pdt, value, FLT_IS_NAN);
} else {
if (isinf(value) != 0) {
/* float is infinite */
convert_special_float(pdt, value, FLT_IS_INF);
}
}
/* process real float */
convert_real_float(pdt, value);
#ifdef DEBUG_VSNPRINTF
printf("convert_float() -> out\n");
#endif
}
/*******************
* pmk_vsnprintf() *
***********************************************************************
DESCR
String formatting function with fixed bufer size and va_list
argument type
IN
buf : buffer location
len : buffer size
fmt : format string
args : list of arguments
OUT
number of characters that would have been written if the buffer had
not been size limited
TODO
wide char support
***********************************************************************/
int pmk_vsnprintf(char *buf, size_t len, const char *fmt, va_list args) {
char *pstr,
*pc = NULL;
flt_t flt_val;
int *pi = NULL,
state = PARSE_CHAR,
modifier = 0,
n_modsave = MDFR_NORMAL,
have_n_conv = 0;
#ifdef HAVE_INTMAX_T
intmax_t *pimt = NULL;
#endif /* HAVE_INTMAX_T */
long *pl = NULL;
#ifdef HAVE_LONG_LONG
long long *pll = NULL;
#endif /* HAVE_LONG_LONG */
#ifdef HAVE_PTRDIFF_T
ptrdiff_t *ppd = NULL;
#endif /* HAVE_PTRDIFF_T */
short *ps = NULL;
signed_t int_val;
size_t *pst = NULL;
unsigned_t uint_val;
void *ptr;
vsnp_t data;
#ifdef HAVE_WCHAR_T
wchar_t *wc;
#endif /* HAVE_WCHAR_T */
#ifdef HAVE_WINT_T
wint_t wi;
#endif /* HAVE_WINT_T */
/* init common data */
data.buf = buf;
data.len = len;
data.cur = 0;
/* loop until the end of the format string is reached */
while (*fmt != '\0') {
switch (state) {
case PARSE_CHAR :
/*
check if we have a conversion sequence
*/
#ifdef DEBUG_VSNPRINTF
printf("Enter state PARSE_CHAR\n");
#endif
if (*fmt == '%') {
data.flags = FLAG_NONE;
data.fwidth = 0; /* -1 ? */
data.prec = -1;
modifier = MDFR_NORMAL;
state = PARSE_FLAGS;
} else {
/* else copy the character in the buffer */
fill_buffer(&data, *fmt);
}
/* next character */
fmt++;
break;
case PARSE_FLAGS :
/*
parse the conversion flags
*/
#ifdef DEBUG_VSNPRINTF
printf("Enter state PARSE_FLAGS\n");
#endif
switch (*fmt) {
case '+' :
SET_FLAG(FLAG_SIGNED);
fmt++;
break;
case '-' :
SET_FLAG(FLAG_LEFT_JUSTIFIED);
fmt++;
break;
case ' ' :
SET_FLAG(FLAG_SPACE_PREFIX);
fmt++;
break;
case '#' :
SET_FLAG(FLAG_ALTERNATIVE_FORM);
fmt++;
break;
case '0' :
SET_FLAG(FLAG_ZERO_PADDING);
fmt++;
break;
default :
state = PARSE_FLD_WIDTH;
}
break;
case PARSE_FLD_WIDTH :
/*
parse the field width
*/
#ifdef DEBUG_VSNPRINTF
printf("Enter state PARSE_FLD_WIDTH\n");
#endif
/* if we got an asterisk */
if (*fmt == '*') {
/* then get the field width from arguments */
data.fwidth = va_arg(args, int);
/* if field width is negative */
if (data.fwidth < 0) {
/* take absolute value for the width */
data.fwidth = data.fwidth * (-1);
/* and set left justify flag */
SET_FLAG(FLAG_LEFT_JUSTIFIED);
}
fmt++;
} else {
/* else take the width from format string */
while ((*fmt >= '0') && (*fmt <= '9')) {
data.fwidth = (data.fwidth * 10) + (int) (*fmt - '0');
fmt++;
}
}
/* ignore 0 flag if - flag is provided */
if (F_IS_SET(FLAG_LEFT_JUSTIFIED)) {
UNSET_FLAG(FLAG_ZERO_PADDING);
}
state = PARSE_DOT;
break;
case PARSE_DOT :
/*
check if the dot of precision field is given
*/
#ifdef DEBUG_VSNPRINTF
printf("Enter state PARSE_DOT\n");
#endif
if (*fmt == '.') {
/* if yes parse the precision field */
fmt++;
data.prec = 0;
state = PARSE_PRECISION;
/*flags = flags & (FLAG_ALL ^ FLAG_ZERO_PADDING);*//* XXX meant for something ??? */
} else {
/* else go parse the modifier */
state = PARSE_LEN_MDFR;
}
break;
case PARSE_PRECISION :
/*
parse precision field
*/
/* if we got an asterisk */
if (*fmt == '*') {
/* then get the precision from arguments */
data.prec = va_arg(args, int);
fmt++;
} else {
/* else take the precision from format string */
while ((*fmt >= '0') && (*fmt <= '9')) {
data.prec = (data.prec * 10) + (int) (*fmt - '0');
fmt++;
}
}
/* go parse the modifier */
state = PARSE_LEN_MDFR;
break;
case PARSE_LEN_MDFR :
/*
parse modifier
*/
#ifdef DEBUG_VSNPRINTF
printf("Enter state PARSE_LEN_MDFR\n");
#endif
switch (*fmt) {
case 'h' :
switch (modifier) {
case MDFR_NORMAL :
modifier = MDFR_SHORT;
fmt++;
break;
case MDFR_SHORT :
modifier = MDFR_CHAR;
state = PARSE_CONV_SPEC;
fmt++;
break;
default :
state = PARSE_CONV_SPEC;
}
break;
case 'l' :
switch (modifier) {
case MDFR_NORMAL :
modifier = MDFR_LONG;
fmt++;
break;
case MDFR_LONG :
#if defined(HAVE_LONG_LONG) || defined(HAVE_UNSIGNED_LONG_LONG)
modifier = MDFR_LONG_LONG;
#endif /* HAVE_LONG_LONG || HAVE_UNSIGNED_LONG_LONG */
state = PARSE_CONV_SPEC;
fmt++;
break;
default :
state = PARSE_CONV_SPEC;
}
break;
case 'j' :
modifier = MDFR_INTMAX;
state = PARSE_CONV_SPEC;
fmt++;
break;
case 'z' :
modifier = MDFR_SIZET;
state = PARSE_CONV_SPEC;
fmt++;
break;
case 't' :
modifier = MDFR_PTRDIFF;
state = PARSE_CONV_SPEC;
fmt++;
break;
case 'L' :
modifier = MDFR_LONG_DBL;
state = PARSE_CONV_SPEC;
fmt++;
break;
default :
state = PARSE_CONV_SPEC;
}
break;
case PARSE_CONV_SPEC :
/*
parse conversion specifier
*/
#ifdef DEBUG_VSNPRINTF
printf("Enter state PARSE_CONV_SPEC\n");
#endif
switch(*fmt) {
case 'd' :
case 'i' :
/*
signed decimal
*/
/* default precision */
if (data.prec < 0) {
data.prec = 1;
}
/* process modifier */
switch (modifier) {
case MDFR_LONG :
int_val = (signed_t) va_arg(args, long);
break;
#ifdef HAVE_LONG_LONG
case MDFR_LONG_LONG :
int_val = (signed_t) va_arg(args, long long);
break;
#endif /* HAVE_LONG_LONG */
case MDFR_SHORT :
case MDFR_CHAR :
/* short and char are promoted to int throught ... */
int_val = (signed_t) va_arg(args, int);
break;
#ifdef HAVE_INTMAX_T
case MDFR_INTMAX :
int_val = (signed_t) va_arg(args, intmax_t);
break;
#endif /* HAVE_INTMAX_T */
case MDFR_SIZET :
int_val = (signed_t) va_arg(args, size_t);
break;
#ifdef HAVE_PTRDIFF_T
case MDFR_PTRDIFF :
int_val = (signed_t) va_arg(args, ptrdiff_t);
break;
#endif /* HAVE_PTRDIFF_T */
default :
int_val = (signed_t) va_arg(args, int);
}
data.base = 10;
convert_sint(&data, int_val);
break;
case 'o' :
case 'u' :
case 'x' :
case 'X' :
/*
unsigned conversion
*/
/* default precision */
if (data.prec < 0) {
data.prec = 1;
}
/* ignore - flag */
UNSET_FLAG(FLAG_SIGNED);
/* process modifier */
switch (modifier) {
case MDFR_LONG :
uint_val = (unsigned_t) va_arg(args, unsigned long);
break;
#ifdef HAVE_UNSIGNED_LONG_LONG
case MDFR_LONG_LONG :
uint_val = (unsigned_t) va_arg(args, unsigned long long);
break;
#endif /* HAVE_UNSIGNED_LONG_LONG */
case MDFR_SHORT :
case MDFR_CHAR :
/* short and char are promoted to int throught ... */
uint_val = (unsigned_t) va_arg(args, unsigned int);
break;
#ifdef HAVE_UINTMAX_T
case MDFR_INTMAX :
uint_val = (unsigned_t) va_arg(args, uintmax_t);
break;
#endif /* HAVE_UINTMAX_T */
case MDFR_SIZET :
uint_val = (unsigned_t) va_arg(args, size_t);
break;
#ifdef HAVE_PTRDIFF_T
case MDFR_PTRDIFF :
uint_val = (unsigned_t) va_arg(args, ptrdiff_t);
break;
#endif /* HAVE_PTRDIFF_T */
default :
uint_val = (unsigned_t) va_arg(args, unsigned int);
}
/* set base */
switch (*fmt) {
case 'o' :
data.base = BASE_OCT;
break;
case 'u' :
data.base = BASE_DEC;
break;
case 'X' :
SET_FLAG(FLAG_UPPERCASE);
/* no break */
case 'x' :
data.base = BASE_HEX;
break;
}
convert_int(&data, uint_val);
break;
case 'F' :
case 'E' :
case 'G' :
case 'A' :
/*
float conversion (uppercase)
*/
SET_FLAG(FLAG_UPPERCASE);
/* no break */
case 'f' :
case 'e' :
case 'g' :
case 'a' :
/*
float conversion (common)
*/
switch (*fmt) {
case 'F' :
case 'f' :
data.base = BASE_DEC;
/* default precision */
if (data.prec < 0) {
data.prec = 6;
}
break;
case 'E' :
case 'e' :
data.base = BASE_DEC;
SET_FLAG(FLAG_EXPONENT);
/* default precision */
if (data.prec < 0) {
data.prec = 6;
}
break;
case 'G' :
case 'g' :
data.base = BASE_DEC;
SET_FLAG(FLAG_G_CONVERSION);
/* default precision */
if (data.prec <= 0) {
data.prec = 6; /* XXX ISO C99 = 1 */
}
break;
case 'A' :
case 'a' :
data.base = BASE_HEX;
SET_FLAG(FLAG_EXPONENT);
/* default precision */
if (data.prec < 0) {
data.prec = 4; /* XXX real default value is ? */
}
break;
}
#ifdef HAVE_LONG_DOUBLE
if (modifier == MDFR_LONG_DBL) {
flt_val = (flt_t) va_arg(args, long double);
} else {
#endif /* HAVE_LONG_DOUBLE */
flt_val = (flt_t) va_arg(args, double);
#ifdef HAVE_LONG_DOUBLE
}
#endif /* HAVE_LONG_DOUBLE */
convert_float(&data, flt_val);
break;
case 'c' :
/*
character conversion
*/
#ifdef HAVE_WINT_T
if (modifier == MDFR_LONG) {
/* wide char */
wi = va_arg(args, wint_t);
/* XXX TODO wide char support */
} else {
#endif /* HAVE_WINT_T */
/* char is promoted to int throught ... */
int_val = (signed_t) va_arg(args, int);
fill_buffer(&data, (char) int_val);
#ifdef HAVE_WINT_T
}
#endif /* HAVE_WINT_T */
break;
case 's' :
/*
string conversion
*/
#ifdef HAVE_WCHAR_T
if (modifier == MDFR_LONG) {
/* wide char */
wc = va_arg(args, wchar_t *);
/* XXX TODO wide char support */
} else {
#endif /* HAVE_WCHAR_T */
pstr = va_arg(args, char *);
if (data.prec <= 0) {
data.prec = len;
}
convert_str(&data, pstr);
#ifdef HAVE_WCHAR_T
}
#endif /* HAVE_WCHAR_T */
break;
case 'p' :
/*
pointer conversion
*/
ptr = va_arg(args, void *);
data.base = BASE_HEX;
/* XXX ?
SET_FLAG(FLAG_ALTERNATIVE_FORM);
*/
convert_int(&data, (unsigned_t) (size_t) ptr);
break;
case 'n' :
/*
pointer to the variable that will receive string size
*/
/* set flag */
have_n_conv = 1;
/* save modifier */
n_modsave = modifier;
/* process modifier */
switch (modifier) {
case MDFR_LONG :
pl = va_arg(args, long *);
break;
#ifdef HAVE_LONG_LONG
case MDFR_LONG_LONG :
pll = va_arg(args, long long *);
break;
#endif /* HAVE_LONG_LONG */
case MDFR_SHORT :
ps = va_arg(args, short *);
break;
case MDFR_CHAR :
pc = va_arg(args, char *);
break;
#ifdef HAVE_INTMAX_T
case MDFR_INTMAX :
pimt = va_arg(args, intmax_t *);
break;
#endif /* HAVE_INTMAX_T */
case MDFR_SIZET :
pst = va_arg(args, size_t *);
break;
#ifdef HAVE_PTRDIFF_T
case MDFR_PTRDIFF :
ppd = va_arg(args, ptrdiff_t *);
break;
#endif /* HAVE_PTRDIFF_T */
default :
pi = va_arg(args, int *);
}
break;
case '%' :
/*
"escaped" conversion character
*/
/* XXX we allow complete specification, != ISO C99 ? */
fill_buffer(&data, *fmt);
break;
}
/* next char */
fmt++;
state = PARSE_CHAR;
break;
}
}
/* NULL terminate the string */
if (len > data.cur) {
buf[data.cur] = '\0';
} else {
buf[len - 1] = '\0';
}
/* if %n provided, write lenght into the pointed area */
if (have_n_conv != 0) {
/* process modifier */
switch (n_modsave) {
case MDFR_LONG :
*pl = (long) data.cur;
break;
#ifdef HAVE_LONG_LONG
case MDFR_LONG_LONG :
*pll = (long long) data.cur;
break;
#endif /* HAVE_LONG_LONG */
case MDFR_SHORT :
*ps = (short) data.cur;
break;
case MDFR_CHAR :
*pc = (char) data.cur;
break;
#ifdef HAVE_INTMAX_T
case MDFR_INTMAX :
*pimt = (intmax_t) data.cur;
break;
#endif /* HAVE_INTMAX_T */
case MDFR_SIZET :
*pst = data.cur;
break;
#ifdef HAVE_PTRDIFF_T
case MDFR_PTRDIFF :
*ppd = (ptrdiff_t) data.cur;
break;
#endif /* HAVE_PTRDIFF_T */
default :
*pi = (int) data.cur;
}
}
/* return what should have been the string lenght if not limited */
return(data.cur);
}
/*****************
* pmk_strlcpy() *
***********************************************************************
DESCR
This function duplicate a string.
IN
ostr : char pointer of source string
OUT
pointer to duplicated string or NULL
***********************************************************************/
char *pmk_strdup(const char *ostr) {
char *pstr;
size_t len;
len = strlen(ostr) + 1;
pstr = (char *) malloc(len);
if (pstr != NULL) {
memcpy(pstr, ostr, len);
}
return(pstr);
}
/*****************
* pmk_strlcpy() *
***********************************************************************
DESCR
This function copy a given number of characters from a source
string to a destination buffer. It also grants that this buffer
will be null terminated.
IN
dst : char pointer of destination buffer
src : char pointer of source string
s : number of characters to copy
OUT
size of the source string
***********************************************************************/
size_t pmk_strlcpy(char *dst, const char *src, size_t s) {
size_t len = 0;
/* loop until we reach the end of the src string */
while (*src != '\0') {
/* if buffer is not full */
if (s > 0) {
if (s == 1) {
/* last character, null terminate */
*dst = '\0';
} else {
/* copy character */
*dst = *src;
}
/* adjust remaining size */
s--;
/* update src string length */
len++;
/* and dst pointer */
dst++;
}
/* increment src pointer */
src++;
}
/* if the end of the buffer has not been reached */
if (s > 0) {
/* last character, null terminate */
*dst = '\0';
}
return(len);
}
/*****************
* pmk_strlcat() *
***********************************************************************
DESCR
This function append a given number of characters from a source
string to the end of a destination buffer. It also grants that
this buffer will be null terminated.
IN
dst : char pointer of destination buffer
src : char pointer of source string
s : number of characters to copy
OUT
size of the source string
***********************************************************************/
size_t pmk_strlcat(char *dst, const char *src, size_t s) {
size_t len;
/* get size of actual string */
len = strlen(dst);
/* start right after the last character */
dst = dst + len;
/* update size */
s = s -len;
/* update len with the result of the string copy */
len = len + pmk_strlcpy(dst, src, s);
return(len);
}
/***************************
* find_parent_separator() *
***********************************************************************
DESCR
try to find the last separator between directory name and file name
IN
path : path string
psep : separator location
plen : path string len
OUT
NONE
***********************************************************************/
static void find_parent_separator(char *path, char **psep, size_t *plen) {
char *psave = NULL;
int have_sep = 0;
/* initialize length */
*plen = 0;
/* initialize separator position */
*psep = NULL;
while (*path != '\0') {
if (*path == '/') {
/* set flag */
have_sep = 1;
/* save position */
psave = path;
} else {
if (have_sep != 0) {
/* had a separator before -> save last position */
*psep = psave;
}
}
/* next character */
path++;
(*plen)++;
}
}
/*****************
* pmk_dirname() *
***********************************************************************
DESCR
XXX
IN
XXX
OUT
XXX
***********************************************************************/
char *pmk_dirname(char *path) {
static char buffer[MAXPATHLEN];
char *pstr,
*psep;
size_t len;
/* start at the begining of the buffer */
pstr = buffer;
if ((path == NULL) || (*path == '\0')) {
*pstr = '.';
pstr++;
} else {
/* look for base/dir separator */
find_parent_separator(path, &psep, &len);
if (psep == NULL) {
/* no valid separator found */
if (*path == '/') {
*pstr = '/';
} else {
*pstr = '.';
}
pstr++;
} else {
while ((psep > path) && (*psep == '/')) {
/* skip trailing slashes */
psep--;
}
/* take care of buffer overflow */
if ((size_t) (psep - path) > sizeof(buffer)) {
return(NULL);
}
/* copy characters until separator position */
while (path <= psep) {
*pstr = *path;
pstr++;
path++;
}
}
}
/* NULL terminate the buffer */
*pstr = '\0';
return(buffer);
}
/******************
* pmk_basename() *
***********************************************************************
DESCR
XXX
IN
XXX
OUT
XXX
***********************************************************************/
char *pmk_basename(char *path) {
static char buffer[MAXPATHLEN];
char *pstr,
*psep,
*pbeg,
*pend;
size_t len;
/* start at the begining of the buffer */
pstr = buffer;
/* if path is a null pointer or empty string */
if ((path == NULL) || (*path == '\0')) {
*pstr = '.';
pstr++;
} else {
/* else look for base/dir separator */
find_parent_separator(path, &psep, &len);
/* if no valid separator is found */
if (psep == NULL) {
/* return the whole string */
pbeg = path;
} else {
/* else return the string starting right after separator */
pbeg = psep + 1;
}
/* compute end of string pointer */
pend = path + len - 1;
while ((pend > pbeg) && (*pend == '/')) {
/* skip trailing slashes */
pend--;
}
/* take care of buffer overflow */
if ((size_t) (pend - pbeg) > sizeof(buffer)) {
return(NULL);
}
/* copy characters until separator position */
while (pbeg <= pend) {
*pstr = *pbeg;
pstr++;
pbeg++;
}
}
/* NULL terminate the buffer */
*pstr = '\0';
return(buffer);
}
/*****************
* pmk_isblank() *
***********************************************************************
DESCR
XXX
IN
XXX
OUT
XXX
***********************************************************************/
int pmk_isblank(int c) {
if (c == ' ' || c == '\t')
return (1);
else
return(0);
}
/******************
* pmk_mkstemps() *
***********************************************************************
DESCR
XXX
IN
XXX
OUT
XXX
***********************************************************************/
int pmk_mkstemps(char *template, int suffixlen) {
struct timeval tv;
char subst[] = "aZbY0cXdWe1VfUgT2hSiRj3QkPlO4mNnMo5LpKqJ6rIsHt7GuFvE8wDxCy9BzA",
*start,
*end,
*p;
int fd,
len,
i;
/* forwarding to end of template */
for (p = template ; *p != '\0' ; p++);
/* increment len to also count end of file character */
suffixlen++;
/* compute (supposed) position of last character to replace */
p = p - suffixlen;
/* check it baby ;) */
if (p < template)
return(-1);
/* set last character position */
end = p;
/* step back until we found the starting character */
for ( ; *p == MKSTEMPS_REPLACE_CHAR && p > template; p--);
/* set fisrt character position */
start = ++p;
/* intialise random() */
len = strlen(subst);
gettimeofday(&tv, NULL);
srandom(tv.tv_sec * tv.tv_usec);
/* lets go replacing the stuff */
for (p = start ; p <= end ; p++) {
/* get random value */
i = (int) random() % len;
/* replace */
*p = subst[i];
}
/* open file */
fd = open(template, O_CREAT|O_EXCL|O_RDWR, S_IRUSR | S_IWUSR);
return(fd);
}
/************************
* compatibility stuff *
***********************************************************************/
/********************
* pmk_stdio.h *
********************/
#ifndef HAVE_VSNPRINTF
int vsnprintf(char *buf, size_t len, const char *fmt, va_list args) {
return(pmk_vsnprintf(buf, len, fmt, args));
}
#endif /* HAVE_VSNPRINTF */
#ifndef HAVE_SNPRINTF
int snprintf(char *str, size_t size, const char *format, ...) {
int rslt;
va_list args;
va_start(args, format);
rslt = vsnprintf(str, size, format, args);
va_end(args);
return(rslt);
}
#endif /* HAVE_SNPRINTF */
/********************
* pmk_string.h *
********************/
#ifndef HAVE_STRDUP
char *strdup(const char *str) {
return(pmk_strdup(str));
}
#endif /* HAVE_STRDUP */
#ifndef HAVE_STRLCAT
size_t strlcat(char *dst, const char *src, size_t s) {
return(pmk_strlcat(dst, src, s));
}
#endif /* HAVE_STRLCAT */
#ifndef HAVE_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t s) {
return(pmk_strlcpy(dst, src, s));
}
#endif /* HAVE_STRLCPY */
/********************
* pmk_libgen.h *
********************/
#ifndef HAVE_LIBGEN_H
char *dirname(char *path) {
return(pmk_dirname(path));
}
char *basename(char *path) {
return(pmk_basename(path));
}
#endif /* HAVE_LIBGEN_H */
/****************
* pmk_ctype.h *
****************/
#ifndef HAVE_ISBLANK
int isblank(int c) {
return(pmk_isblank(c));
}
#endif /* HAVE_ISBLANK */
/********************
* pmk_unistd.h *
********************/
#ifndef HAVE_MKSTEMPS
int mkstemps(char *template, int suffixlen) {
return(pmk_mkstemps(template, suffixlen));
}
#endif /* HAVE_MKSTEMPS */
/********************************
* boolean string functions *
***********************************************************************/
/****************
* snprintf_b() *
***********************************************************************
DESCR
boolean snprintf
IN
buf : buffer location
siz : buffer size
fmt : format string
... : argument list
OUT
boolean value
***********************************************************************/
bool snprintf_b(char *buf, size_t siz, const char *fmt, ...) {
bool rslt;
va_list ap;
va_start(ap, fmt);
if (vsnprintf(buf, siz, fmt, ap) >= (int) siz)
rslt = false;
else
rslt = true;
va_end(ap);
return(rslt);
}
/***************
* strlcat_b() *
***********************************************************************
DESCR
boolean strlcat
IN
dst : destination buffer
src : source string
siz : size of destination buffer
OUT
boolean
***********************************************************************/
bool strlcat_b(char *dst, const char *src, size_t siz) {
if (strlcat(dst, src, siz) >= siz)
return(false);
else
return(true);
}
/***************
* strlcpy_b() *
***********************************************************************
DESCR
boolean strlcpy
IN
dst : destination buffer
src : source string
siz : size of destination buffer
OUT
boolean
***********************************************************************/
bool strlcpy_b(char *dst, const char *src, size_t siz) {
if (strlcpy(dst, src, siz) >= siz)
return(false);
else
return(true);
}
/* vim: set noexpandtab tabstop=4 softtabstop=4 shiftwidth=4: */
syntax highlighted by Code2HTML, v. 0.9.1