mirror of
git://git.acid.vegas/unrealircd.git
synced 2024-11-14 20:16:42 +00:00
225 lines
5.0 KiB
C
225 lines
5.0 KiB
C
/*
|
|
* Unreal Internet Relay Chat Daemon, src/ircsprintf.c
|
|
*
|
|
* (C) Copyright 2013
|
|
*
|
|
* Author: Falcon Darkstar Momot based upon earlier code by Carlo Wood in 1997:
|
|
* 1024/624ACAD5 1997/01/26 Carlo Wood, Run on IRC <carlo@runaway.xs4all.nl>
|
|
* Key fingerprint = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
|
|
*
|
|
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
#include "ircsprintf.h"
|
|
#include <stdio.h>
|
|
|
|
/** Optimized version of vsnprintf() for us.
|
|
* ircvsnprintf is optimized for the formats: %s, %c, %d, %i, %u, %lu and %lld.
|
|
* If you only use these format string types then this function is significantly
|
|
* faster than regular vsnprintf().
|
|
* When it encounters any other format type or things like padding, precision,
|
|
* etc. then it will resort to calling vsnprintf(), so no problem.
|
|
*/
|
|
char *ircvsnprintf(char *str, size_t size, const char *format, va_list vl)
|
|
{
|
|
char *str_begin = str;
|
|
char c;
|
|
const char *end = str+size-1; /* for comparison, not dereferencing. It is the last position a null can go. */
|
|
char scratch_buffer[32]; /* large enough for 64 bit integer as a string */
|
|
|
|
if (!size) return str;
|
|
|
|
while (str!=end && (c = *format++))
|
|
{
|
|
if (c == '%')
|
|
{
|
|
c = *format++;
|
|
if (c == 's')
|
|
{
|
|
/* %s - string */
|
|
const char *p1 = va_arg(vl, const char *);
|
|
while (str!=end && *p1) *str++ = *p1++;
|
|
continue;
|
|
}
|
|
else if (c == 'c')
|
|
{
|
|
/* %c - single character */
|
|
*str++ = (char)va_arg(vl, int);
|
|
continue;
|
|
}
|
|
else if (c == 'd' || c == 'i')
|
|
{
|
|
/* %d and %i - integer */
|
|
char *t;
|
|
int v = va_arg(vl, int);
|
|
int i = 0;
|
|
size_t len;
|
|
if (v==0)
|
|
{
|
|
*str++ = '0';
|
|
continue;
|
|
}
|
|
t = scratch_buffer + sizeof(scratch_buffer);
|
|
if (v<0)
|
|
{
|
|
*str++ = '-';
|
|
if (str==end) break;
|
|
while (v)
|
|
{
|
|
*--t = '0' - (v%10);
|
|
v/=10;
|
|
}
|
|
} else {
|
|
while (v)
|
|
{
|
|
*--t = (v%10) + '0';
|
|
v/=10;
|
|
}
|
|
}
|
|
|
|
len = sizeof(scratch_buffer)-(t-scratch_buffer);
|
|
if ((str+len)>end) break;
|
|
for (i = 0; i < len; i++)
|
|
*str++=t[i];
|
|
continue;
|
|
}
|
|
else if (c == 'l')
|
|
{
|
|
if (format[0] == 'l' && format[1] == 'd')
|
|
{
|
|
/* %lld - long long */
|
|
char *t;
|
|
long long v = va_arg(vl, long long);
|
|
int i = 0;
|
|
size_t len;
|
|
|
|
format += 2;
|
|
|
|
if (v==0)
|
|
{
|
|
*str++ = '0';
|
|
continue;
|
|
}
|
|
t = scratch_buffer + sizeof(scratch_buffer);
|
|
if (v<0)
|
|
{
|
|
*str++ = '-';
|
|
if (str==end) break;
|
|
while (v)
|
|
{
|
|
*--t = '0' - (v%10);
|
|
v/=10;
|
|
}
|
|
} else {
|
|
while (v)
|
|
{
|
|
*--t = (v%10) + '0';
|
|
v/=10;
|
|
}
|
|
}
|
|
|
|
len = sizeof(scratch_buffer)-(t-scratch_buffer);
|
|
if ((str+len)>end) break;
|
|
for (i = 0; i < len; i++)
|
|
*str++=t[i];
|
|
continue;
|
|
}
|
|
if (*format == 'u')
|
|
{
|
|
/* %lu - unsigned long */
|
|
char *t;
|
|
unsigned long v = va_arg(vl, unsigned long);
|
|
int i = 0;
|
|
size_t len;
|
|
|
|
format++;
|
|
if (v==0)
|
|
{
|
|
*str++ = '0';
|
|
continue;
|
|
}
|
|
|
|
t = scratch_buffer + sizeof(scratch_buffer);
|
|
while (v)
|
|
{
|
|
*--t = (v%10) + '0';
|
|
v/=10;
|
|
}
|
|
|
|
len = sizeof(scratch_buffer)-(t-scratch_buffer);
|
|
if ((str+len)>end) break;
|
|
for (i = 0; i < len; i++)
|
|
*str++=t[i];
|
|
continue;
|
|
}
|
|
}
|
|
else if (c == 'u')
|
|
{
|
|
/* %u - unsigned integer */
|
|
char *t;
|
|
unsigned int v = va_arg(vl, unsigned int);
|
|
int i = 0;
|
|
size_t len;
|
|
if (v==0)
|
|
{
|
|
*str++ = '0';
|
|
continue;
|
|
}
|
|
|
|
t = scratch_buffer + sizeof(scratch_buffer);
|
|
while (v)
|
|
{
|
|
*--t = (v%10) + '0';
|
|
v/=10;
|
|
}
|
|
|
|
len = sizeof(scratch_buffer)-(t-scratch_buffer);
|
|
if ((str+len)>end) break;
|
|
for (i = 0; i < len; i++)
|
|
*str++=t[i];
|
|
continue;
|
|
}
|
|
else if (c == '%')
|
|
{
|
|
/* %% - literal percent character */
|
|
*str++ = '%';
|
|
continue;
|
|
}
|
|
else if (!c)
|
|
break; /* A % at the end of the format string (illegal, skipped) */
|
|
|
|
/* The default case, when we cannot handle the % format:
|
|
* Stop what we are doing and pass control to the real vsnprintf()
|
|
*/
|
|
format -= 2;
|
|
vsnprintf(str, (size_t)(end-str+1), format, vl);
|
|
return str_begin;
|
|
}
|
|
*str++ = c;
|
|
}
|
|
*str = 0;
|
|
return str_begin;
|
|
}
|
|
|
|
char *ircsnprintf(char *str, size_t size, const char *format, ...) {
|
|
va_list vl;
|
|
char *ret;
|
|
va_start(vl, format);
|
|
ret = ircvsnprintf(str, size, format, vl);
|
|
va_end(vl);
|
|
return ret;
|
|
}
|