Rev 455 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* linux/lib/vsprintf.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
* Wirzenius wrote this portably, Torvalds fucked it up :-)
*/
/*
* Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
* - changed to provide snprintf and vsnprintf functions
*/
#include <linuxcomp.h>
#include <stdarg.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <asm/div64.h>
/**
* simple_strtoul - convert a string to an unsigned long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
unsigned long simple_strtoul
(const char *cp
,char **endp
,unsigned int base
)
{
unsigned long result
= 0,value
;
if (!base
) {
base
= 10;
if (*cp
== '0') {
base
= 8;
cp
++;
if ((*cp
== 'x') && isxdigit(cp
[1])) {
cp
++;
base
= 16;
}
}
}
while (isxdigit(*cp
) &&
(value
= isdigit(*cp
) ? *cp
-'0' : toupper(*cp
)-'A'+10) < base
) {
result
= result
*base
+ value
;
cp
++;
}
if (endp
)
*endp
= (char *)cp
;
return result
;
}
EXPORT_SYMBOL
(simple_strtoul
);
/**
* simple_strtol - convert a string to a signed long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
long simple_strtol
(const char *cp
,char **endp
,unsigned int base
)
{
if(*cp
=='-')
return -simple_strtoul
(cp
+1,endp
,base
);
return simple_strtoul
(cp
,endp
,base
);
}
EXPORT_SYMBOL
(simple_strtol
);
/**
* simple_strtoull - convert a string to an unsigned long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
unsigned long long simple_strtoull
(const char *cp
,char **endp
,unsigned int base
)
{
unsigned long long result
= 0,value
;
if (!base
) {
base
= 10;
if (*cp
== '0') {
base
= 8;
cp
++;
if ((*cp
== 'x') && isxdigit(cp
[1])) {
cp
++;
base
= 16;
}
}
}
while (isxdigit(*cp
) && (value
= isdigit(*cp
) ? *cp
-'0' : (islower(*cp
)
? toupper(*cp
) : *cp
)-'A'+10) < base
) {
result
= result
*base
+ value
;
cp
++;
}
if (endp
)
*endp
= (char *)cp
;
return result
;
}
EXPORT_SYMBOL
(simple_strtoull
);
/**
* simple_strtoll - convert a string to a signed long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
long long simple_strtoll
(const char *cp
,char **endp
,unsigned int base
)
{
if(*cp
=='-')
return -simple_strtoull
(cp
+1,endp
,base
);
return simple_strtoull
(cp
,endp
,base
);
}
static int skip_atoi
(const char **s
)
{
int i
=0;
while (isdigit(**s
))
i
= i
*10 + *((*s
)++) - '0';
return i
;
}
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#define SPECIAL 32 /* 0x */
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
static char * number
(char * buf
, char * end
, unsigned long long num
, int base
, int size
, int precision
, int type
)
{
char c
,sign
,tmp
[66];
const char *digits
;
static const char small_digits
[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static const char large_digits
[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i
;
digits
= (type
& LARGE
) ? large_digits
: small_digits
;
if (type
& LEFT
)
type
&= ~ZEROPAD
;
if (base
< 2 || base
> 36)
return 0;
c
= (type
& ZEROPAD
) ? '0' : ' ';
sign
= 0;
if (type
& SIGN
) {
if ((signed long long) num
< 0) {
sign
= '-';
num
= - (signed long long) num
;
size
--;
} else if (type
& PLUS
) {
sign
= '+';
size
--;
} else if (type
& SPACE
) {
sign
= ' ';
size
--;
}
}
if (type
& SPECIAL
) {
if (base
== 16)
size
-= 2;
else if (base
== 8)
size
--;
}
i
= 0;
if (num
== 0)
tmp
[i
++]='0';
else while (num
!= 0)
tmp
[i
++] = digits
[do_div
(num
,base
)];
if (i
> precision
)
precision
= i
;
size
-= precision
;
if (!(type
&(ZEROPAD
+LEFT
))) {
while(size
-->0) {
if (buf
<= end
)
*buf
= ' ';
++buf
;
}
}
if (sign
) {
if (buf
<= end
)
*buf
= sign
;
++buf
;
}
if (type
& SPECIAL
) {
if (base
==8) {
if (buf
<= end
)
*buf
= '0';
++buf
;
} else if (base
==16) {
if (buf
<= end
)
*buf
= '0';
++buf
;
if (buf
<= end
)
*buf
= digits
[33];
++buf
;
}
}
if (!(type
& LEFT
)) {
while (size
-- > 0) {
if (buf
<= end
)
*buf
= c
;
++buf
;
}
}
while (i
< precision
--) {
if (buf
<= end
)
*buf
= '0';
++buf
;
}
while (i
-- > 0) {
if (buf
<= end
)
*buf
= tmp
[i
];
++buf
;
}
while (size
-- > 0) {
if (buf
<= end
)
*buf
= ' ';
++buf
;
}
return buf
;
}
/**
* vsnprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
* @fmt: The format string to use
* @args: Arguments for the format string
*
* Call this function if you are already dealing with a va_list.
* You probably want snprintf instead.
*/
int vsnprintf26
(char *buf
, size_t size
, const char *fmt
, va_list args
)
{
int len
;
unsigned long long num
;
int i
, base
;
char *str
, *end
, c
;
const char *s
;
int flags
; /* flags to number() */
int field_width
; /* width of output field */
int precision
; /* min. # of digits for integers; max
number of chars for from string */
int qualifier
; /* 'h', 'l', or 'L' for integer fields */
/* 'z' support added 23/7/1999 S.H. */
/* 'z' changed to 'Z' --davidm 1/25/99 */
str
= buf
;
end
= buf
+ size
- 1;
if (end
< buf
- 1) {
end
= ((void *) -1);
size
= end
- buf
+ 1;
}
for (; *fmt
; ++fmt
) {
if (*fmt
!= '%') {
if (str
<= end
)
*str
= *fmt
;
++str
;
continue;
}
/* process flags */
flags
= 0;
repeat
:
++fmt
; /* this also skips first '%' */
switch (*fmt
) {
case '-': flags
|= LEFT
; goto repeat
;
case '+': flags
|= PLUS
; goto repeat
;
case ' ': flags
|= SPACE
; goto repeat
;
case '#': flags
|= SPECIAL
; goto repeat
;
case '0': flags
|= ZEROPAD
; goto repeat
;
}
/* get field width */
field_width
= -1;
if (isdigit(*fmt
))
field_width
= skip_atoi
(&fmt
);
else if (*fmt
== '*') {
++fmt
;
/* it's the next argument */
field_width
= va_arg(args
, int);
if (field_width
< 0) {
field_width
= -field_width
;
flags
|= LEFT
;
}
}
/* get the precision */
precision
= -1;
if (*fmt
== '.') {
++fmt
;
if (isdigit(*fmt
))
precision
= skip_atoi
(&fmt
);
else if (*fmt
== '*') {
++fmt
;
/* it's the next argument */
precision
= va_arg(args
, int);
}
if (precision
< 0)
precision
= 0;
}
/* get the conversion qualifier */
qualifier
= -1;
if (*fmt
== 'h' || *fmt
== 'l' || *fmt
== 'L' ||
*fmt
=='Z' || *fmt
== 'z') {
qualifier
= *fmt
;
++fmt
;
if (qualifier
== 'l' && *fmt
== 'l') {
qualifier
= 'L';
++fmt
;
}
}
/* default base */
base
= 10;
switch (*fmt
) {
case 'c':
if (!(flags
& LEFT
)) {
while (--field_width
> 0) {
if (str
<= end
)
*str
= ' ';
++str
;
}
}
c
= (unsigned char) va_arg(args
, int);
if (str
<= end
)
*str
= c
;
++str
;
while (--field_width
> 0) {
if (str
<= end
)
*str
= ' ';
++str
;
}
continue;
case 's':
s
= va_arg(args
, char *);
if ((unsigned long)s
< PAGE_SIZE
)
s
= "<NULL>";
len
= strnlen
(s
, precision
);
if (!(flags
& LEFT
)) {
while (len
< field_width
--) {
if (str
<= end
)
*str
= ' ';
++str
;
}
}
for (i
= 0; i
< len
; ++i
) {
if (str
<= end
)
*str
= *s
;
++str
; ++s
;
}
while (len
< field_width
--) {
if (str
<= end
)
*str
= ' ';
++str
;
}
continue;
case 'p':
if (field_width
== -1) {
field_width
= 2*sizeof(void *);
flags
|= ZEROPAD
;
}
str
= number
(str
, end
,
(unsigned long) va_arg(args
, void *),
16, field_width
, precision
, flags
);
continue;
case 'n':
/* FIXME:
* What does C99 say about the overflow case here? */
if (qualifier
== 'l') {
long * ip
= va_arg(args
, long *);
*ip
= (str
- buf
);
} else if (qualifier
== 'Z' || qualifier
== 'z') {
size_t * ip
= va_arg(args
, size_t *);
*ip
= (str
- buf
);
} else {
int * ip
= va_arg(args
, int *);
*ip
= (str
- buf
);
}
continue;
case '%':
if (str
<= end
)
*str
= '%';
++str
;
continue;
/* integer number formats - set up the flags and "break" */
case 'o':
base
= 8;
break;
case 'X':
flags
|= LARGE
;
case 'x':
base
= 16;
break;
case 'd':
case 'i':
flags
|= SIGN
;
case 'u':
break;
default:
if (str
<= end
)
*str
= '%';
++str
;
if (*fmt
) {
if (str
<= end
)
*str
= *fmt
;
++str
;
} else {
--fmt
;
}
continue;
}
if (qualifier
== 'L')
num
= va_arg(args
, long long);
else if (qualifier
== 'l') {
num
= va_arg(args
, unsigned long);
if (flags
& SIGN
)
num
= (signed long) num
;
} else if (qualifier
== 'Z' || qualifier
== 'z') {
num
= va_arg(args
, size_t);
} else if (qualifier
== 'h') {
num
= (unsigned short) va_arg(args
, int);
if (flags
& SIGN
)
num
= (signed short) num
;
} else {
num
= va_arg(args
, unsigned int);
if (flags
& SIGN
)
num
= (signed int) num
;
}
str
= number
(str
, end
, num
, base
,
field_width
, precision
, flags
);
}
if (str
<= end
)
*str
= '\0';
else if (size
> 0)
/* don't write out a null byte if the buf size is zero */
*end
= '\0';
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str
-buf
;
}
EXPORT_SYMBOL
(vsnprintf26
);
/**
* snprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
* @fmt: The format string to use
* @...: Arguments for the format string
*/
int snprintf26
(char * buf
, size_t size
, const char *fmt
, ...
)
{
va_list args
;
int i
;
va_start(args
, fmt
);
i
=vsnprintf26
(buf
,size
,fmt
,args
);
va_end(args
);
return i
;
}
EXPORT_SYMBOL
(snprintf26
);
/**
* vsprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @fmt: The format string to use
* @args: Arguments for the format string
*
* Call this function if you are already dealing with a va_list.
* You probably want sprintf instead.
*/
int vsprintf26
(char *buf
, const char *fmt
, va_list args
)
{
return vsnprintf26
(buf
, 0xFFFFFFFFUL
, fmt
, args
);
}
EXPORT_SYMBOL
(vsprintf26
);
/**
* sprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @fmt: The format string to use
* @...: Arguments for the format string
*/
int sprintf26
(char * buf
, const char *fmt
, ...
)
{
va_list args
;
int i
;
va_start(args
, fmt
);
i
=vsprintf26
(buf
,fmt
,args
);
va_end(args
);
return i
;
}
EXPORT_SYMBOL
(sprintf26
);
/**
* vsscanf - Unformat a buffer into a list of arguments
* @buf: input buffer
* @fmt: format of buffer
* @args: arguments
*/
int vsscanf26
(const char * buf
, const char * fmt
, va_list args
)
{
const char *str
= buf
;
char *next
;
char digit
;
int num
= 0;
int qualifier
;
int base
;
int field_width
;
int is_sign
= 0;
while(*fmt
&& *str
) {
/* skip any white space in format */
/* white space in format matchs any amount of
* white space, including none, in the input.
*/
if (isspace(*fmt
)) {
while (isspace(*fmt
))
++fmt
;
while (isspace(*str
))
++str
;
}
/* anything that is not a conversion must match exactly */
if (*fmt
!= '%' && *fmt
) {
if (*fmt
++ != *str
++)
break;
continue;
}
if (!*fmt
)
break;
++fmt
;
/* skip this conversion.
* advance both strings to next white space
*/
if (*fmt
== '*') {
while (!isspace(*fmt
) && *fmt
)
fmt
++;
while (!isspace(*str
) && *str
)
str
++;
continue;
}
/* get field width */
field_width
= -1;
if (isdigit(*fmt
))
field_width
= skip_atoi
(&fmt
);
/* get conversion qualifier */
qualifier
= -1;
if (*fmt
== 'h' || *fmt
== 'l' || *fmt
== 'L' ||
*fmt
== 'Z' || *fmt
== 'z') {
qualifier
= *fmt
;
fmt
++;
}
base
= 10;
is_sign
= 0;
if (!*fmt
|| !*str
)
break;
switch(*fmt
++) {
case 'c':
{
char *s
= (char *) va_arg(args
,char*);
if (field_width
== -1)
field_width
= 1;
do {
*s
++ = *str
++;
} while(field_width
-- > 0 && *str
);
num
++;
}
continue;
case 's':
{
char *s
= (char *) va_arg(args
, char *);
if(field_width
== -1)
field_width
= ((int)(~0U
>>1));
/* first, skip leading white space in buffer */
while (isspace(*str
))
str
++;
/* now copy until next white space */
while (*str
&& !isspace(*str
) && field_width
--) {
*s
++ = *str
++;
}
*s
= '\0';
num
++;
}
continue;
case 'n':
/* return number of characters read so far */
{
int *i
= (int *)va_arg(args
,int*);
*i
= str
- buf
;
}
continue;
case 'o':
base
= 8;
break;
case 'x':
case 'X':
base
= 16;
break;
case 'i':
base
= 0;
case 'd':
is_sign
= 1;
case 'u':
break;
case '%':
/* looking for '%' in str */
if (*str
++ != '%')
return num
;
continue;
default:
/* invalid format; stop here */
return num
;
}
/* have some sort of integer conversion.
* first, skip white space in buffer.
*/
while (isspace(*str
))
str
++;
digit
= *str
;
if (is_sign
&& digit
== '-')
digit
= *(str
+ 1);
if (!digit
|| (base
== 16 && !isxdigit(digit
))
|| (base
== 10 && !isdigit(digit
))
|| (base
== 8 && (!isdigit(digit
) || digit
> '7'))
|| (base
== 0 && !isdigit(digit
)))
break;
switch(qualifier
) {
case 'h':
if (is_sign
) {
short *s
= (short *) va_arg(args
,short *);
*s
= (short) simple_strtol
(str
,&next
,base
);
} else {
unsigned short *s
= (unsigned short *) va_arg(args
, unsigned short *);
*s
= (unsigned short) simple_strtoul
(str
, &next
, base
);
}
break;
case 'l':
if (is_sign
) {
long *l
= (long *) va_arg(args
,long *);
*l
= simple_strtol
(str
,&next
,base
);
} else {
unsigned long *l
= (unsigned long*) va_arg(args
,unsigned long*);
*l
= simple_strtoul
(str
,&next
,base
);
}
break;
case 'L':
if (is_sign
) {
long long *l
= (long long*) va_arg(args
,long long *);
*l
= simple_strtoll
(str
,&next
,base
);
} else {
unsigned long long *l
= (unsigned long long*) va_arg(args
,unsigned long long*);
*l
= simple_strtoull
(str
,&next
,base
);
}
break;
case 'Z':
case 'z':
{
size_t *s
= (size_t*) va_arg(args
,size_t*);
*s
= (size_t) simple_strtoul
(str
,&next
,base
);
}
break;
default:
if (is_sign
) {
int *i
= (int *) va_arg(args
, int*);
*i
= (int) simple_strtol
(str
,&next
,base
);
} else {
unsigned int *i
= (unsigned int*) va_arg(args
, unsigned int*);
*i
= (unsigned int) simple_strtoul
(str
,&next
,base
);
}
break;
}
num
++;
if (!next
)
break;
str
= next
;
}
return num
;
}
EXPORT_SYMBOL
(vsscanf26
);
/**
* sscanf - Unformat a buffer into a list of arguments
* @buf: input buffer
* @fmt: formatting of buffer
* @...: resulting arguments
*/
int sscanf26
(const char * buf
, const char * fmt
, ...
)
{
va_list args
;
int i
;
va_start(args
,fmt
);
i
= vsscanf26
(buf
,fmt
,args
);
va_end(args
);
return i
;
}
EXPORT_SYMBOL
(sscanf26
);
/**
* strlcpy - Copy a %NUL terminated string into a sized buffer
* @dest: Where to copy the string to
* @src: Where to copy the string from
* @size: size of destination buffer
*
* Compatible with *BSD: the result is always a valid
* NUL-terminated string that fits in the buffer (unless,
* of course, the buffer size is zero). It does not pad
* out the result like strncpy() does.
*/
size_t strlcpy
(char *dest
, const char *src
, size_t size
)
{
size_t ret
= strlen(src
);
if (size
) {
size_t len
= (ret
>= size
) ? size
-1 : ret
;
memcpy(dest
, src
, len
);
dest
[len
] = '\0';
}
return ret
;
}
EXPORT_SYMBOL
(strlcpy
);