| /* |
| * "$Id: vsnprintf.c 7903 2010-11-28 21:06:39Z matt $" |
| * |
| * snprintf() and vsnprintf() functions for the Fast Light Tool Kit (FLTK). |
| * |
| * Copyright 1998-2010 by Bill Spitzak and others. |
| * |
| * 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. |
| * |
| * Please report all bugs and problems on the following page: |
| * |
| * http://www.fltk.org/str.php |
| */ |
| |
| #include <stdio.h> |
| #include "flstring.h" |
| |
| #ifdef HAVE_SYS_STDTYPES_H |
| # include <sys/stdtypes.h> |
| #endif /* HAVE_SYS_STDTYPES_H */ |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| int fl_vsnprintf(char* buffer, size_t bufsize, const char* format, va_list ap) { |
| char *bufptr, /* Pointer to position in buffer */ |
| *bufend, /* Pointer to end of buffer */ |
| sign, /* Sign of format width */ |
| size, /* Size character (h, l, L) */ |
| type; /* Format type character */ |
| int width, /* Width of field */ |
| prec; /* Number of characters of precision */ |
| char tformat[100], /* Temporary format string for sprintf() */ |
| *tptr, /* Pointer into temporary format */ |
| temp[1024]; /* Buffer for formatted numbers */ |
| char *s; /* Pointer to string */ |
| int slen; /* Length of string */ |
| int bytes; /* Total number of bytes needed */ |
| |
| |
| /* |
| * Loop through the format string, formatting as needed... |
| */ |
| |
| bufptr = buffer; |
| bufend = buffer + bufsize - 1; |
| bytes = 0; |
| |
| while (*format) { |
| if (*format == '%') { |
| tptr = tformat; |
| *tptr++ = *format++; |
| |
| if (*format == '%') { |
| if (bufptr && bufptr < bufend) *bufptr++ = *format; |
| bytes ++; |
| format ++; |
| continue; |
| } else if (strchr(" -+#\'", *format)) { |
| *tptr++ = *format; |
| sign = *format++; |
| } else sign = 0; |
| |
| if (*format == '*') { |
| /* Get width from argument... */ |
| format ++; |
| width = va_arg(ap, int); |
| snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); |
| tptr += strlen(tptr); |
| } else { |
| width = 0; |
| while (isdigit(*format & 255)) { |
| if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; |
| width = width * 10 + *format++ - '0'; |
| } |
| } |
| |
| if (*format == '.') { |
| if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; |
| format ++; |
| |
| if (*format == '*') { |
| /* Get precision from argument... */ |
| format ++; |
| prec = va_arg(ap, int); |
| snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); |
| tptr += strlen(tptr); |
| } else { |
| prec = 0; |
| while (isdigit(*format & 255)) { |
| if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; |
| prec = prec * 10 + *format++ - '0'; |
| } |
| } |
| } else prec = -1; |
| |
| size = '\0'; |
| |
| if (*format == 'l' && format[1] == 'l') { |
| size = 'L'; |
| if (tptr < (tformat + sizeof(tformat) - 2)) { |
| *tptr++ = 'l'; |
| *tptr++ = 'l'; |
| } |
| format += 2; |
| } else if (*format == 'h' || *format == 'l' || *format == 'L') { |
| if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; |
| size = *format++; |
| } |
| |
| if (!*format) break; |
| |
| if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; |
| type = *format++; |
| *tptr = '\0'; |
| |
| switch (type) { |
| case 'E' : /* Floating point formats */ |
| case 'G' : |
| case 'e' : |
| case 'f' : |
| case 'g' : |
| if ((width + 2) > sizeof(temp)) break; |
| |
| sprintf(temp, tformat, va_arg(ap, double)); |
| |
| bytes += strlen(temp); |
| |
| if (bufptr) { |
| if ((bufptr + strlen(temp)) > bufend) { |
| strncpy(bufptr, temp, (size_t)(bufend - bufptr)); |
| bufptr = bufend; |
| } else { |
| strcpy(bufptr, temp); |
| bufptr += strlen(temp); |
| } |
| } |
| break; |
| |
| case 'B' : /* Integer formats */ |
| case 'X' : |
| case 'b' : |
| case 'd' : |
| case 'i' : |
| case 'o' : |
| case 'u' : |
| case 'x' : |
| if ((width + 2) > sizeof(temp)) break; |
| |
| #ifdef HAVE_LONG_LONG |
| if (size == 'L') |
| sprintf(temp, tformat, va_arg(ap, long long)); |
| else |
| #endif /* HAVE_LONG_LONG */ |
| if (size == 'l') |
| sprintf(temp, tformat, va_arg(ap, long)); |
| else |
| sprintf(temp, tformat, va_arg(ap, int)); |
| |
| bytes += strlen(temp); |
| |
| if (bufptr) { |
| if ((bufptr + strlen(temp)) > bufend) { |
| strncpy(bufptr, temp, (size_t)(bufend - bufptr)); |
| bufptr = bufend; |
| } else { |
| strcpy(bufptr, temp); |
| bufptr += strlen(temp); |
| } |
| } |
| break; |
| |
| case 'p' : /* Pointer value */ |
| if ((width + 2) > sizeof(temp)) break; |
| |
| sprintf(temp, tformat, va_arg(ap, void *)); |
| |
| bytes += strlen(temp); |
| |
| if (bufptr) { |
| if ((bufptr + strlen(temp)) > bufend) { |
| strncpy(bufptr, temp, (size_t)(bufend - bufptr)); |
| bufptr = bufend; |
| } else { |
| strcpy(bufptr, temp); |
| bufptr += strlen(temp); |
| } |
| } |
| break; |
| |
| case 'c' : /* Character or character array */ |
| bytes += width; |
| |
| if (bufptr) { |
| if (width <= 1) *bufptr++ = va_arg(ap, int); |
| else { |
| if ((bufptr + width) > bufend) width = bufend - bufptr; |
| |
| memcpy(bufptr, va_arg(ap, char *), (size_t)width); |
| bufptr += width; |
| } |
| } |
| break; |
| |
| case 's' : /* String */ |
| if ((s = va_arg(ap, char *)) == NULL) s = "(null)"; |
| |
| slen = strlen(s); |
| if (slen > width && prec != width) width = slen; |
| |
| bytes += width; |
| |
| if (bufptr) { |
| if ((bufptr + width) > bufend) width = bufend - bufptr; |
| |
| if (slen > width) slen = width; |
| |
| if (sign == '-') { |
| strncpy(bufptr, s, (size_t)slen); |
| memset(bufptr + slen, ' ', (size_t)(width - slen)); |
| } else { |
| memset(bufptr, ' ', (size_t)(width - slen)); |
| strncpy(bufptr + width - slen, s, (size_t)slen); |
| } |
| |
| bufptr += width; |
| } |
| break; |
| |
| case 'n' : /* Output number of chars so far */ |
| *(va_arg(ap, int *)) = bytes; |
| break; |
| } |
| } else { |
| bytes ++; |
| |
| if (bufptr && bufptr < bufend) *bufptr++ = *format; |
| format ++; |
| } |
| } |
| |
| /* |
| * Nul-terminate the string and return the number of characters needed. |
| */ |
| |
| if (bufptr) *bufptr = '\0'; |
| |
| return (bytes); |
| } |
| |
| int fl_snprintf(char* str, size_t size, const char* fmt, ...) { |
| int ret; |
| va_list ap; |
| va_start(ap, fmt); |
| ret = vsnprintf(str, size, fmt, ap); |
| va_end(ap); |
| return ret; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /* |
| * End of "$Id: vsnprintf.c 7903 2010-11-28 21:06:39Z matt $". |
| */ |
| |