// // aegis - project change supervisor // Copyright (C) 1991-1994, 1997, 1999, 2002-2006, 2008, 2012 Peter Miller // Copyright (C) 2009 Walter Franzini // // 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 3 of the License, 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, see . // #include #include #include #include #include #include #include #include #include #include // // size to grow memory by // #define QUANTUM 200 // // maximum width for numbers // #define MAX_WIDTH (QUANTUM - 1) // // the buffer for storing results // static size_t tmplen; static size_t length; static char *tmp; // // NAME // bigger - grow dynamic memory buffer // // SYNOPSIS // int bigger(void); // // DESCRIPTION // The bigger function is used to grow the dynamic memory buffer // used by vmprintf to store the formatting results. // The buffer is increased by QUANTUM bytes. // // RETURNS // int; zero if failed to realloc memory, non-zero if successful. // // CAVEATS // The existing buffer is still valid after failure. // static int bigger(void) { char *hold; size_t nbytes; nbytes = tmplen + QUANTUM; errno = 0; hold = (char *)realloc(tmp, nbytes); if (!hold) { if (!errno) errno = ENOMEM; return 0; } tmplen = nbytes; tmp = hold; return 1; } /** * @brief * construct formatting specifier string * * The build_fake function is used to construct a format * specification string from the arguments presented. This is * used to guarantee exact replication of sprintf behaviour. * * @param fake * buffer to store results * @param fake_len * size of buffer to store results * @param flag * the flag specified (zero if not), e.g. '#' * @param width * the width specified (zero if not) * @param prec * the precision specified (zero if not) * @param qual * the qualifier specified (zero if not), e.g. 'l' * @param spec * the formatting specifier specified (e.g. 'd') */ static void build_fake(char *fake, size_t fake_len, int flag, int width, int precision, int qualifier, int specifier) { char *fp; fp = fake; *fp++ = '%'; if (flag) *fp++ = flag; if (width > 0) { snprintf(fp, fake + fake_len - fp - 5, "%d", width); fp += strlen(fp); } *fp++ = '.'; snprintf(fp, fake + fake_len - fp - 3, "%d", precision); fp += strlen(fp); switch (qualifier) { case '\0': break; case 'H': *fp++ = 'h'; *fp++ = 'h'; break; case 'L': *fp++ = 'l'; *fp++ = 'l'; break; default: *fp++ = qualifier; break; } *fp++ = specifier; *fp = 0; } // // NAME // vmprintf_errok - build a formatted string in dynamic memory // // SYNOPSIS // char *vmprintf_errok(char *fmt, va_list ap); // // DESCRIPTION // The vmprintf_errok function is used to build a formatted string // in memory. It understands all of the ANSI standard sprintf // formatting directives. Additionally, "%S" may be used to // manipulate (string_ty *) strings. // // ARGUMENTS // fmt - string specifying formatting to perform // ap - arguments of types as indicated by the format string // // RETURNS // char *; pointer to buffer containing formatted string // NULL if there is an error (sets errno) // // CAVEATS // The contents of the buffer pointed to will change between calls // to vmprintf_errok. The buffer itself may move between calls to // vmprintf_errok. DO NOT hand the result of vmprintf_errok to // free(). // char * vmprintf_errok(const char *fmt, va_list ap) { int width; int width_set; int prec; int prec_set; int c; const char *s; int qualifier; int flag; char fake[QUANTUM - 1]; // // Build the result string in a temporary buffer. // Grow the temporary buffer as necessary. // // It is important to only make one pass across the variable argument // list. Behaviour is undefined for more than one pass. // if (!tmplen) { tmplen = 500; errno = 0; tmp = (char *)malloc(tmplen); if (!tmp) { if (!errno) errno = ENOMEM; return 0; } } length = 0; s = fmt; while (*s) { c = *s++; if (c != '%') { normal: if (length >= tmplen && !bigger()) return 0; tmp[length++] = c; continue; } c = *s++; // // get optional flag // switch (c) { case '+': case '-': case '#': case '0': case ' ': flag = c; c = *s++; break; default: flag = 0; break; } // // get optional width // width = 0; width_set = 0; switch (c) { case '*': width = va_arg(ap, int); if (width < 0) { flag = '-'; width = -width; } c = *s++; width_set = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (;;) { width = width * 10 + c - '0'; c = *s++; switch (c) { default: break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; } break; } width_set = 1; break; default: break; } // // get optional precision // prec = 0; prec_set = 0; if (c == '.') { c = *s++; switch (c) { default: prec_set = 1; break; case '*': c = *s++; prec = va_arg(ap, int); if (prec < 0) { prec = 0; break; } prec_set = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (;;) { prec = prec * 10 + c - '0'; c = *s++; switch (c) { default: break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; } break; } prec_set = 1; break; } } // // get the optional qualifier // switch (c) { default: qualifier = 0; break; case 'l': case 'h': case 'L': case 'j': case 'z': case 't': qualifier = c; c = *s++; break; } // // Convert the standard qualifier for the long long type // ('ll') into the one used by mprintf ('L', a GNU extension). // if (qualifier == 'l' && c == 'l') { qualifier = 'L'; c = *s++; } if (qualifier == 'h' && c == 'h') { qualifier = 'H'; c = *s++; } // // get conversion specifier // switch (c) { default: errno = EINVAL; return 0; case '%': goto normal; case 'c': { int a; char num[MAX_WIDTH + 1]; size_t len; a = (unsigned char)va_arg(ap, int); if (!prec_set) prec = 1; if (width > MAX_WIDTH) width = MAX_WIDTH; if (prec > MAX_WIDTH) prec = MAX_WIDTH; build_fake(fake, sizeof(fake), flag, width, prec, 0, c); // g++ -Wformat-nonlitteral will warn here, it is safe to ignore snprintf(num, sizeof(num), fake, a); len = strlen(num); assert(len < QUANTUM); if (length + len > tmplen && !bigger()) return 0; memcpy(tmp + length, num, len); length += len; } break; case 'd': case 'i': { intmax_t a; char num[MAX_WIDTH + 1]; size_t len; switch (qualifier) { case 'h': a = (short)va_arg(ap, int); break; case 'H': // same a hh, see above a = (signed char)va_arg(ap, int); break; case 'j': a = va_arg(ap, intmax_t); break; case 'l': a = va_arg(ap, long); break; case 'L': // same as ll, see above a = va_arg(ap, long long); break; case 't': a = va_arg(ap, ptrdiff_t); break; case 'z': a = va_arg(ap, size_t); break; default: a = va_arg(ap, int); break; } if (!prec_set) prec = 1; if (width > MAX_WIDTH) width = MAX_WIDTH; if (prec > MAX_WIDTH) prec = MAX_WIDTH; build_fake(fake, sizeof(fake), flag, width, prec, 'j', c); // g++ -Wformat-nonlitteral will warn here, it is safe to ignore snprintf(num, sizeof(num), fake, a); len = strlen(num); assert(len < QUANTUM); if (length + len > tmplen && !bigger()) return 0; memcpy(tmp + length, num, len); length += len; } break; case 'e': case 'f': case 'g': case 'E': case 'F': case 'G': { double a; char num[MAX_WIDTH + 1]; size_t len; // // Ignore "long double" for now, // traditional implementations no grok. // a = va_arg(ap, double); if (!prec_set) prec = 6; if (width > MAX_WIDTH) width = MAX_WIDTH; if (prec > MAX_WIDTH) prec = MAX_WIDTH; build_fake(fake, sizeof(fake), flag, width, prec, 0, c); // g++ -Wformat-nonlitteral will warn here, it is safe to ignore snprintf(num, sizeof(num), fake, a); len = strlen(num); assert(len < QUANTUM); if (length + len > tmplen && !bigger()) return 0; memcpy(tmp + length, num, len); length += len; } break; case 'n': switch (qualifier) { case 'h': { short *a = va_arg(ap, short *); *a = length; } break; case 'H': { char *a = va_arg(ap, char *); *a = length; } break; case 'j': { intmax_t *a = va_arg(ap, intmax_t *); *a = length; } break; case 'l': { long *a = va_arg(ap, long *); *a = length; } break; case 'L': { long long *a = va_arg(ap, long long *); *a = length; } break; case 't': { ptrdiff_t *a = va_arg(ap, ptrdiff_t *); *a = length; } break; case 'z': { size_t *a = va_arg(ap, size_t *); *a = length; } break; default: { int *a = va_arg(ap, int *); *a = length; } break; } break; case 'u': case 'o': case 'x': case 'X': { uintmax_t a; char num[MAX_WIDTH + 1]; size_t len; switch (qualifier) { case 'h': a = (unsigned short)va_arg(ap, unsigned int); break; case 'H': a = (unsigned char)va_arg(ap, unsigned int); break; case 'j': a = va_arg(ap, uintmax_t); break; case 'l': a = va_arg(ap, unsigned long); break; case 'L': a = va_arg(ap, unsigned long long); break; case 't': a = va_arg(ap, ptrdiff_t); break; case 'z': a = va_arg(ap, size_t); break; default: a = va_arg(ap, unsigned int); break; } if (!prec_set) prec = 1; if (prec > MAX_WIDTH) prec = MAX_WIDTH; if (width > MAX_WIDTH) width = MAX_WIDTH; build_fake(fake, sizeof(fake), flag, width, prec, 'l', c); // g++ -Wformat-nonlitteral will warn here, it is safe to ignore snprintf(num, sizeof(num), fake, a); len = strlen(num); assert(len < QUANTUM); if (length + len > tmplen && !bigger()) return 0; memcpy(tmp + length, num, len); length += len; } break; case 'p': { void *a = va_arg(ap, void *); char num[MAX_WIDTH + 1]; snprintf(num, sizeof(num), "%p", a); size_t len = strlen(num); assert(len < QUANTUM); if (length + len > tmplen && !bigger()) return 0; memcpy(tmp + length, num, len); length += len; } break; case 's': { char *a; size_t len; a = va_arg(ap, char *); if (prec_set) { char *ep; ep = (char *)memchr(a, 0, prec); if (ep) len = ep - a; else len = prec; } else len = strlen(a); if (!prec_set || len < (size_t)prec) prec = len; if (!width_set || width < prec) width = prec; len = width; while (length + len > tmplen) { if (!bigger()) return 0; } if (flag != '-') { while (width > prec) { tmp[length++] = ' '; width--; } } memcpy(tmp + length, a, prec); length += prec; width -= prec; if (flag == '-') { while (width > 0) { tmp[length++] = ' '; width--; } } } break; case 'S': { string_ty *a; size_t len; a = va_arg(ap, string_ty *); len = a->str_length; if (!prec_set) prec = len; if (len < (size_t)prec) prec = len; if (!width_set) width = prec; if (width < prec) width = prec; len = width; while (length + len > tmplen) { if (!bigger()) return 0; } if (flag != '-') { while (width > prec) { tmp[length++] = ' '; width--; } } memcpy(tmp + length, a->str_text, prec); length += prec; width -= prec; if (flag == '-') { while (width > 0) { tmp[length++] = ' '; width--; } } } break; } } // // append a trailing NUL // if (length >= tmplen && !bigger()) return 0; tmp[length] = 0; // // return the temporary string // return tmp; } // // NAME // mprintf_errok - build a formatted string in dynamic memory // // SYNOPSIS // char *mprintf_errok(char *fmt, ...); // // DESCRIPTION // The mprintf_errok function is used to build a formatted string // in memory. It understands all of the ANSI standard sprintf // formatting directives. Additionally, "%S" may be used to // manipulate (string_ty *) strings. // // ARGUMENTS // fmt - string spefiifying formatting to perform // ... - arguments of types as indicated by the format string // // RETURNS // char *; pointer to buffer containing formatted string // NULL if there is an error (sets errno) // // CAVEATS // The contents of the buffer pointed to will change between calls // to mprintf_errok. The buffer itself may move between calls to // mprintf_errok. DO NOT hand the result of mprintf_errok to // free(). // char * mprintf_errok(const char *fmt, ...) { char *result; va_list ap; va_start(ap, fmt); result = vmprintf_errok(fmt, ap); va_end(ap); return result; } // // NAME // vmprintf - build a formatted string in dynamic memory // // SYNOPSIS // char *vmprintf(char *fmt, va_list ap); // // DESCRIPTION // The vmprintf function is used to build a formatted string in memory. // It understands all of the ANSI standard sprintf formatting directives. // Additionally, "%S" may be used to manipulate (string_ty *) strings. // // ARGUMENTS // fmt - string spefiifying formatting to perform // ap - arguments of types as indicated by the format string // // RETURNS // char *; pointer to buffer containing formatted string // // CAVEATS // On error, prints a fatal error message and exists; does not return. // // The contents of the buffer pointed to will change between calls // to vmprintf. The buffer itself may move between calls to vmprintf. // DO NOT hand the result of vmprintf to free(). // char * vmprintf(const char *fmt, va_list ap) { char *result; result = vmprintf_errok(fmt, ap); if (!result) nfatal("mprintf \"%s\"", fmt); return result; } // // NAME // mprintf - build a formatted string in dynamic memory // // SYNOPSIS // char *mprintf(char *fmt, ...); // // DESCRIPTION // The mprintf function is used to build a formatted string in memory. // It understands all of the ANSI standard sprintf formatting directives. // Additionally, "%S" may be used to manipulate (string_ty *) strings. // // ARGUMENTS // fmt - string spefiifying formatting to perform // ... - arguments of types as indicated by the format string // // RETURNS // char *; pointer to buffer containing formatted string // // CAVEATS // On error, prints a fatal error message and exists; does not return. // // The contents of the buffer pointed to will change between calls // to mprintf. The buffer itself may move between calls to mprintf. // DO NOT hand the result of mprintfe to free(). // char * mprintf(const char *fmt, ...) { char *result; va_list ap; va_start(ap, fmt); result = vmprintf(fmt, ap); va_end(ap); return result; } // // NAME // vmprintf_str - build a formatted string in dynamic memory // // SYNOPSIS // char *vmprintf_str(char *fmt, va_list ap); // // DESCRIPTION // The vmprintf_str function is used to build a formatted string in memory. // It understands all of the ANSI standard sprintf formatting directives. // Additionally, "%S" may be used to manipulate (string_ty *) strings. // // ARGUMENTS // fmt - string spefiifying formatting to perform // ap - arguments of types as indicated by the format string // // RETURNS // string_ty *; string containing formatted string // // CAVEATS // On error, prints a fatal error message and exists; does not return. // // It is the resposnsibility of the caller to invoke str_free to release // the results when finished with. // string_ty * vmprintf_str(const char *fmt, va_list ap) { if (!vmprintf_errok(fmt, ap)) nfatal("mprintf \"%s\"", fmt); return str_n_from_c(tmp, length); } // vim: set ts=8 sw=4 et :