/* * This file is derived from GLIB by Thomas Sailer, * * * GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include static unsigned int printf_string_upper_bound (const char *format, va_list args) { unsigned int len = 1; while (*format) { int long_int = 0; int extra_long = 0; char c; c = *format++; if (c == '%') { int done = 0; while (*format && !done) { switch (*format++) { char *string_arg; case '*': len += va_arg (args, int); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* add specified format length, since it might exceed the * size we assume it to have. */ format -= 1; len += strtol (format, (char**) &format, 10); break; case 'h': /* ignore short int flag, since all args have at least the * same size as an int */ break; case 'l': if (long_int) extra_long = 1; /* linux specific */ else long_int = 1; break; case 'q': case 'L': long_int = 1; extra_long = 1; break; case 's': string_arg = va_arg (args, char *); if (string_arg) len += strlen (string_arg); else { /* add enough padding to hold "(null)" identifier */ len += 16; } done = 1; break; case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': if (long_int) (void) va_arg (args, long); else (void) va_arg (args, int); len += extra_long ? 64 : 32; done = 1; break; case 'D': case 'O': case 'U': (void) va_arg (args, long); len += 32; done = 1; break; case 'e': case 'E': case 'f': case 'g': (void) va_arg (args, double); len += extra_long ? 64 : 32; done = 1; break; case 'c': (void) va_arg (args, int); len += 1; done = 1; break; case 'p': case 'n': (void) va_arg (args, void*); len += 32; done = 1; break; case '%': len += 1; done = 1; break; default: /* ignore unknow/invalid flags */ break; } } } else len += 1; } return len; } /* WARNING: This does only work for i386 (plus a few others, but not eg. alpha) !!!! */ #define VA_COPY(x,y) (x) = (y) static char *strdup_vprintf (const char *format, va_list args1) { char *buffer; va_list args2; VA_COPY (args2, args1); buffer = malloc(printf_string_upper_bound (format, args1)); if (buffer) vsprintf (buffer, format, args2); va_end (args2); return buffer; } int snprintf (char *str, size_t n, char const *fmt, ...) { char *printed; va_list args; int len; va_start(args, fmt); printed = strdup_vprintf (fmt, args); va_end (args); if (!printed) return -1; len = strlen(printed); strncpy(str, printed, n); str[n-1] = '\0'; free(printed); return len >= n ? -1 : len; } int vsnprintf (char *str, size_t n, char const *fmt, va_list args) { char *printed; int len; printed = strdup_vprintf (fmt, args); if (!printed) return -1; len = strlen(printed); strncpy(str, printed, n); str[n-1] = '\0'; free(printed); return len >= n ? -1 : len; }