rockbox/firmware/common/sprintf.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

292 lines
6.9 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Gary Czvitkovicz
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* Minimal printf and snprintf formatting functions
*
* These support %c %s %d and %x
* Field width and zero-padding flag only
*/
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include "file.h" /* for write(), used in fprintf() */
#include "sprintf.h" /* to allow the simulator magic */
static const char hexdigit[] = "0123456789ABCDEF";
static int format(
/* call 'push()' for each output letter */
int (*push)(void *userp, unsigned char data),
void *userp,
const char *fmt,
va_list ap)
{
char *str;
char tmpbuf[12], pad;
int ch, width, val, sign, precision;
long lval, lsign;
unsigned int uval;
unsigned long ulval;
bool ok = true;
tmpbuf[sizeof tmpbuf - 1] = '\0';
while ((ch = *fmt++) != '\0' && ok)
{
if (ch == '%')
{
ch = *fmt++;
pad = ' ';
if (ch == '0')
pad = '0';
width = 0;
while (ch >= '0' && ch <= '9')
{
width = 10*width + ch - '0';
ch = *fmt++;
}
precision = 0;
if(ch == '.')
{
ch = *fmt++;
while (ch >= '0' && ch <= '9')
{
precision = 10*precision + ch - '0';
ch = *fmt++;
}
} else {
precision = INT_MAX;
}
str = tmpbuf + sizeof tmpbuf - 1;
switch (ch)
{
case 'c':
*--str = va_arg (ap, int);
break;
case 's':
str = va_arg (ap, char*);
break;
case 'd':
val = sign = va_arg (ap, int);
if (val < 0)
val = -val;
do
{
*--str = (val % 10) + '0';
val /= 10;
}
while (val > 0);
if (sign < 0)
*--str = '-';
break;
case 'u':
uval = va_arg(ap, unsigned int);
do
{
*--str = (uval % 10) + '0';
uval /= 10;
}
while (uval > 0);
break;
case 'x':
case 'X':
uval = va_arg (ap, int);
do
{
*--str = hexdigit[uval & 0xf];
uval >>= 4;
}
while (uval);
break;
case 'l':
ch = *fmt++;
switch(ch) {
case 'x':
case 'X':
ulval = va_arg (ap, long);
do
{
*--str = hexdigit[ulval & 0xf];
ulval >>= 4;
}
while (ulval);
break;
case 'd':
lval = lsign = va_arg (ap, long);
if (lval < 0)
lval = -lval;
do
{
*--str = (lval % 10) + '0';
lval /= 10;
}
while (lval > 0);
if (lsign < 0)
*--str = '-';
break;
case 'u':
ulval = va_arg(ap, unsigned long);
do
{
*--str = (ulval % 10) + '0';
ulval /= 10;
}
while (ulval > 0);
break;
default:
*--str = 'l';
*--str = ch;
}
break;
default:
*--str = ch;
break;
}
if (width > 0)
{
width -= strlen (str);
while (width-- > 0 && ok)
ok=push(userp, pad);
}
while (*str != '\0' && ok && precision--)
ok=push(userp, *str++);
}
else
ok=push(userp, ch);
}
return ok; /* true means good */
}
#if !defined(SIMULATOR) || !defined(linux)
/* ALSA library requires a more advanced snprintf, so let's not
override it in simulator for Linux. Note that Cygwin requires
our snprintf or it produces garbled output after a while. */
struct for_snprintf {
unsigned char *ptr; /* where to store it */
int bytes; /* amount already stored */
int max; /* max amount to store */
};
static int sprfunc(void *ptr, unsigned char letter)
{
struct for_snprintf *pr = (struct for_snprintf *)ptr;
if(pr->bytes < pr->max) {
*pr->ptr = letter;
pr->ptr++;
pr->bytes++;
return true;
}
return false; /* filled buffer */
}
int snprintf(char *buf, size_t size, const char *fmt, ...)
{
bool ok;
va_list ap;
struct for_snprintf pr;
pr.ptr = (unsigned char *)buf;
pr.bytes = 0;
pr.max = size;
va_start(ap, fmt);
ok = format(sprfunc, &pr, fmt, ap);
va_end(ap);
/* make sure it ends with a trailing zero */
pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0';
return pr.bytes;
}
int vsnprintf(char *buf, int size, const char *fmt, va_list ap)
{
bool ok;
struct for_snprintf pr;
pr.ptr = (unsigned char *)buf;
pr.bytes = 0;
pr.max = size;
ok = format(sprfunc, &pr, fmt, ap);
/* make sure it ends with a trailing zero */
pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0';
return pr.bytes;
}
#endif /* Linux SIMULATOR */
struct for_fprintf {
int fd; /* where to store it */
int bytes; /* amount stored */
};
static int fprfunc(void *pr, unsigned char letter)
{
struct for_fprintf *fpr = (struct for_fprintf *)pr;
int rc = write(fpr->fd, &letter, 1);
if(rc > 0) {
fpr->bytes++; /* count them */
return true; /* we are ok */
}
return false; /* failure */
}
int fdprintf(int fd, const char *fmt, ...)
{
bool ok;
va_list ap;
struct for_fprintf fpr;
fpr.fd=fd;
fpr.bytes=0;
va_start(ap, fmt);
ok = format(fprfunc, &fpr, fmt, ap);
va_end(ap);
return fpr.bytes; /* return 0 on error */
}