1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
/*
* snprintf() / vsnprintf() re-implementation by Frank Denis <j at pureftpd dot org>
*
* These functions understand :
* - characters ("%c") .
* - space padding ("%3d", "%-3s") .
* - zero padding ("%04d") .
* - explicit '+' ("%+d", "%+3.2f") .
* - length restrictions ("%.30s", "%-42.30s", "%.4d") .
* - int, long, long long types ("%lld", "%-3ld", "%i") .
* - unsigned int, long, long long types ("%llu", "%-3lu", "%u") .
* - hex and octal unsigned types ("%llX", "%04x", "%-3o") .
* - double and long double types ("%f", "%Lf") .
* - floating point frac restrictions ("%.2f") .
* - combinations of everything ("%-8.5llo") .
*
* Nothing more. Return value is <size> if an overflow occurred, or the
* copied size if no overflow occurred (mostly compatible with C99
* snprintf() behavior, except that it doesn't return any value larger
* than <size>).
*
* These functions are portable, they are twice faster than their BSD and GNU
* implementations, and they don't tamper with errno. But they only know
* a limited subset of what a full-implementation is supposed to do.
*
* It's enough for Pure-FTPd, though.
*/
#include <config.h>
#include "ftpd.h"
#ifdef WITH_DMALLOC
# include <dmalloc.h>
#endif
#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
/*
* add a string to the buffer
* \param zero if this is non-zero, we pad with zeroes, else we pad
* with a blank.
* \param maxlen sets the maximum size of the string to be added
*/
static void fakesnprintf_addstr(char **str, size_t *size, const char *pnt,
size_t maxlen, size_t padlen,
unsigned char zero, unsigned char minuspad)
{
size_t maxlenc;
/* prepare to cut off string if longer than maxlen */
maxlenc = strlen(pnt);
if (maxlen > 0U && maxlen < maxlenc) {
maxlenc = maxlen;
}
if (padlen > 0U && minuspad == 0U && padlen > maxlenc) {
size_t maxlenp = padlen - maxlenc;
if (maxlenp > *size) {
maxlenp = *size;
}
if (maxlenp > 0U) {
memset(*str, zero != 0 ? '0' : ' ', maxlenp);
(*size) -= maxlenp;
(*str) += maxlenp;
}
}
if (maxlenc > *size) {
maxlenc = *size;
}
if (maxlenc > 0U) {
memcpy(*str, pnt, maxlenc);
(*size) -= maxlenc;
(*str) += maxlenc;
}
if (padlen > 0U && minuspad > 0U && padlen > maxlenc) {
size_t maxlenp = padlen - maxlenc;
if (maxlenp > *size) {
maxlenp = *size;
}
if (maxlenp > 0U) {
memset(*str, ' ', maxlenp);
(*size) -= maxlenp;
(*str) += maxlenp;
}
}
}
int fakesnprintf_vsnprintf(char * const str_, const size_t size_,
const char *format, va_list va)
{
char *str;
size_t size;
size_t maxlen;
size_t padlen;
unsigned char longs;
unsigned char zero;
unsigned char minuspad;
unsigned char hasmaxlen;
unsigned char plussign;
str = str_;
size = size_;
str_[size_ - 1U] = 1;
while (size > 0U && *format != 0) {
if (*format != '%') {
*str++ = *format++;
size--;
continue;
}
longs = 0U;
zero = 0U;
minuspad = 0U;
maxlen = 0U;
padlen = 0U;
hasmaxlen = 0U;
plussign = 0U;
for (;;) {
breakpoint_nextspecial_inc:
format++;
breakpoint_nextspecial_noinc:
switch (*format) {
case 0:
goto breakpoint_end;
case '%':
*str++ = '%';
size--;
goto breakpoint_next;
case 'c': {
int val;
val = va_arg(va, int);
*str++ = (char) val;
size--;
goto breakpoint_next;
}
case 'l': case 'L':
longs++;
goto breakpoint_nextspecial_inc;
case '0':
zero++;
goto breakpoint_nextspecial_inc;
case '.':
format++;
hasmaxlen = 1U;
while ((unsigned char) *format >= '0' &&
(unsigned char) *format <= '9') {
maxlen *= 10U;
maxlen += (*format - '0');
format++;
}
goto breakpoint_nextspecial_noinc;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
do {
padlen *= 10U;
padlen += *format - '0';
format++;
} while ((unsigned char) *format >= '0' &&
(unsigned char) *format <= '9');
goto breakpoint_nextspecial_noinc;
case '-':
minuspad++;
format++;
goto breakpoint_nextspecial_noinc;
case '+':
plussign++;
format++;
goto breakpoint_nextspecial_noinc;
case 's': {
const char *pnt;
pnt = va_arg(va, const char *);
if (pnt == NULL) {
pnt = "<NULL>";
}
fakesnprintf_addstr(&str, &size, pnt, maxlen, padlen,
zero, minuspad);
goto breakpoint_next;
}
case 'u': case 'o': case 'x': case 'X': {
unsigned long long val;
char vals[256];
char *valspnt = vals + sizeof vals;
const char *basics;
unsigned int base;
switch (longs) {
case 2:
val = va_arg(va, unsigned long long);
break;
case 1:
val = (unsigned long long) va_arg(va, unsigned long);
break;
default:
val = (unsigned long long) va_arg(va, unsigned int);
}
basics = "0123456789abcdef";
switch (*format) {
case 'o':
base = 8U;
break;
case 'X':
basics = "0123456789ABCDEF";
case 'x':
base = 16U;
break;
default:
base = 10U;
}
*--valspnt = 0;
do {
*--valspnt = basics[val % base];
val /= base;
} while (valspnt != &vals[0] && val > 0ULL);
fakesnprintf_addstr(&str, &size, valspnt, maxlen, padlen,
zero, minuspad);
goto breakpoint_next;
}
case 'd': case 'i': {
long long val;
unsigned char minussign = 0U;
char vals[256];
char *valspnt = vals + sizeof vals;
switch (longs) {
case 2:
val = va_arg(va, long long);
break;
case 1:
val = (long long) va_arg(va, long);
break;
default:
val = (long long) va_arg(va, int);
}
if (val < 0LL) {
minussign++;
val = -val;
}
*--valspnt = 0;
do {
*--valspnt = "0123456789"[val % 10LL];
val /= 10LL;
} while (valspnt != &vals[1] && val > 0LL);
if (minussign != 0) {
*--valspnt = '-';
} else if (plussign != 0) {
*--valspnt = '+';
}
fakesnprintf_addstr(&str, &size, valspnt, maxlen, padlen,
zero, minuspad);
goto breakpoint_next;
}
case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': {
unsigned int nfrac = 6U;
long double val;
unsigned long long vali;
unsigned char minussign = 0U;
char vals[512];
char *valspnt = vals + sizeof vals / 2U;
char *valsleft;
if (longs != 0) {
val = va_arg(va, long double);
} else {
val = va_arg(va, double);
}
if (val < 0.0L) {
minussign++;
val = -val;
}
vali = (unsigned long long) val;
do {
*--valspnt = '0' + vali % 10ULL;
vali /= 10ULL;
} while (valspnt != &vals[1] && vali > 0ULL);
if (minussign != 0) {
*--valspnt = '-';
} else if (plussign != 0) {
*--valspnt = '+';
}
valsleft = valspnt;
valspnt = vals + sizeof vals / 2U;
if (maxlen > (sizeof vals / 2U) - 3U) {
nfrac = (sizeof vals / 2U) - 3U;
} else if (hasmaxlen != 0U) {
nfrac = maxlen;
}
if (nfrac > 0U) {
*valspnt++ = '.';
}
while (nfrac > 0U) {
nfrac--;
val *= 10.0L;
*valspnt++ = '0' + (((unsigned long long) val) % 10U);
}
*valspnt = 0;
fakesnprintf_addstr(&str, &size, valsleft, sizeof vals,
padlen, zero, minuspad);
goto breakpoint_next;
}
}
}
breakpoint_next:
format++;
}
breakpoint_end:
if (str_[size_ - 1U] != 1) {
str_[size_ - 1U] = 0;
return (int) size_;
}
*str = 0;
return (int) (size_ - size);
}
int fakesnprintf_snprintf(char * const str, const size_t size,
const char * const format, ...)
{
int ret;
va_list va;
va_start(va, format);
ret = fakesnprintf_vsnprintf(str, size, format, va);
va_end(va);
return ret;
}
#endif /* !HAVE_SNPRINTF */
|