| /* |
| * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $" |
| * |
| * String functions for Mini-XML, a small XML-like file parsing library. |
| * |
| * Copyright 2003-2010 by Michael R Sweet. |
| * |
| * These coded instructions, statements, and computer programs are the |
| * property of Michael R Sweet and are protected by Federal copyright |
| * law. Distribution and use rights are outlined in the file "COPYING" |
| * which should have been included with this file. If this file is |
| * missing or damaged, see the license at: |
| * |
| * http://www.minixml.org/ |
| * |
| * Contents: |
| * |
| * _mxml_snprintf() - Format a string. |
| * _mxml_strdup() - Duplicate a string. |
| * _mxml_strdupf() - Format and duplicate a string. |
| * _mxml_vsnprintf() - Format a string into a fixed size buffer. |
| * _mxml_vstrdupf() - Format and duplicate a string. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "config.h" |
| |
| |
| /* |
| * The va_copy macro is part of C99, but many compilers don't implement it. |
| * Provide a "direct assignment" implmentation when va_copy isn't defined... |
| */ |
| |
| #ifndef va_copy |
| # ifdef __va_copy |
| # define va_copy(dst,src) __va_copy(dst,src) |
| # else |
| # define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list)) |
| # endif /* __va_copy */ |
| #endif /* va_copy */ |
| |
| |
| #ifndef HAVE_SNPRINTF |
| /* |
| * '_mxml_snprintf()' - Format a string. |
| */ |
| |
| int /* O - Number of bytes formatted */ |
| _mxml_snprintf(char *buffer, /* I - Output buffer */ |
| size_t bufsize, /* I - Size of output buffer */ |
| const char *format, /* I - Printf-style format string */ |
| ...) /* I - Additional arguments as needed */ |
| { |
| va_list ap; /* Argument list */ |
| int bytes; /* Number of bytes formatted */ |
| |
| |
| va_start(ap, format); |
| bytes = vsnprintf(buffer, bufsize, format, ap); |
| va_end(ap); |
| |
| return (bytes); |
| } |
| #endif /* !HAVE_SNPRINTF */ |
| |
| |
| /* |
| * '_mxml_strdup()' - Duplicate a string. |
| */ |
| |
| #ifndef HAVE_STRDUP |
| char * /* O - New string pointer */ |
| _mxml_strdup(const char *s) /* I - String to duplicate */ |
| { |
| char *t; /* New string pointer */ |
| |
| |
| if (s == NULL) |
| return (NULL); |
| |
| if ((t = malloc(strlen(s) + 1)) == NULL) |
| return (NULL); |
| |
| return (strcpy(t, s)); |
| } |
| #endif /* !HAVE_STRDUP */ |
| |
| |
| /* |
| * '_mxml_strdupf()' - Format and duplicate a string. |
| */ |
| |
| char * /* O - New string pointer */ |
| _mxml_strdupf(const char *format, /* I - Printf-style format string */ |
| ...) /* I - Additional arguments as needed */ |
| { |
| va_list ap; /* Pointer to additional arguments */ |
| char *s; /* Pointer to formatted string */ |
| |
| |
| /* |
| * Get a pointer to the additional arguments, format the string, |
| * and return it... |
| */ |
| |
| va_start(ap, format); |
| s = _mxml_vstrdupf(format, ap); |
| va_end(ap); |
| |
| return (s); |
| } |
| |
| |
| #ifndef HAVE_VSNPRINTF |
| /* |
| * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. |
| */ |
| |
| int /* O - Number of bytes formatted */ |
| _mxml_vsnprintf(char *buffer, /* O - Output buffer */ |
| size_t bufsize, /* O - Size of output buffer */ |
| const char *format, /* I - Printf-style format string */ |
| va_list ap) /* I - Pointer to additional arguments */ |
| { |
| 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; |
| |
| 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 */ |
| 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. |
| */ |
| |
| *bufptr = '\0'; |
| |
| return (bytes); |
| } |
| #endif /* !HAVE_VSNPRINTF */ |
| |
| |
| /* |
| * '_mxml_vstrdupf()' - Format and duplicate a string. |
| */ |
| |
| char * /* O - New string pointer */ |
| _mxml_vstrdupf(const char *format, /* I - Printf-style format string */ |
| va_list ap) /* I - Pointer to additional arguments */ |
| { |
| int bytes; /* Number of bytes required */ |
| char *buffer, /* String buffer */ |
| temp[256]; /* Small buffer for first vsnprintf */ |
| va_list apcopy; /* Copy of argument list */ |
| |
| |
| /* |
| * First format with a tiny buffer; this will tell us how many bytes are |
| * needed... |
| */ |
| |
| va_copy(apcopy, ap); |
| bytes = vsnprintf(temp, sizeof(temp), format, apcopy); |
| |
| if (bytes < sizeof(temp)) |
| { |
| /* |
| * Hey, the formatted string fits in the tiny buffer, so just dup that... |
| */ |
| |
| return (strdup(temp)); |
| } |
| |
| /* |
| * Allocate memory for the whole thing and reformat to the new, larger |
| * buffer... |
| */ |
| |
| if ((buffer = calloc(1, bytes + 1)) != NULL) |
| vsnprintf(buffer, bytes + 1, format, ap); |
| |
| /* |
| * Return the new string... |
| */ |
| |
| return (buffer); |
| } |
| |
| |
| /* |
| * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $". |
| */ |