This is http_bprintf.c in view mode; [Download] [Up]
/* * printf() style routines stolen from FastCGI * Copyright (c) 1996 Open Market, Inc. */ /* * Modified to work with Apache buffering routines by Ben Laurie * <ben@algroup.co.uk>. * * Modifications Copyright (C) 1996 Ben Laurie. * * History: * 18 May 1996 Initial revision [Ben Laurie] * */ #include "httpd.h" #include <math.h> #if !defined(max) #define max(a,b) (a > b ? a : b) #endif #ifdef NO_LONG_DOUBLE #define LONG_DOUBLE double #else #define LONG_DOUBLE long double #endif #define FALSE 0 #define TRUE 1 #define PRINTF_BUFFLEN 100 /* * More than sufficient space for all unmodified conversions * except %s and %f. */ #define FMT_BUFFLEN 25 /* * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop */ #define NULL_STRING "(null)" /* * String displayed if given a NULL pointer. */ /* * Copy n characters from *srcPtr to *destPtr, then increment * both *srcPtr and *destPtr by n. */ static void CopyAndAdvance(char **destPtr, const char **srcPtr, int n) { char *dest = *destPtr; const char *src = *srcPtr; int i; for (i = 0; i < n; i++) *dest++ = *src++; *destPtr = dest; *srcPtr = src; } API_EXPORT_NONSTD(int) vbprintf(BUFF *bp, const char *format, va_list arg) { const char *f,*fStop,*percentPtr,*p; char *fmtBuffPtr, *buffPtr; int op, performedOp, sizeModifier, buffLen, specifierLength; int fastPath, n, buffReqd, minWidth, precision, expon; int buffCount = 0; int auxBuffLen = 0; char *auxBuffPtr = NULL; int streamCount = 0; char fmtBuff[FMT_BUFFLEN]; char buff[PRINTF_BUFFLEN]; int intArg; short shortArg; long longArg; unsigned unsignedArg; unsigned long uLongArg; unsigned short uShortArg; char *charPtrArg = NULL; void *voidPtrArg; int *intPtrArg; long *longPtrArg; short *shortPtrArg; double doubleArg = 0.0; LONG_DOUBLE lDoubleArg = 0.0; fmtBuff[0] = '%'; f=format; fStop = f + strlen(f); while (f != fStop) { percentPtr = memchr(f, '%', fStop - f); if(percentPtr == NULL) percentPtr = fStop; if(percentPtr != f) { if(bwrite(bp,f,percentPtr - f) < 0) goto ErrorReturn; streamCount += percentPtr - f; f = percentPtr; if(f == fStop) break; } fastPath = TRUE; /* * The following loop always executes either once or twice. */ for (;;) { if(fastPath) { /* * Fast path: Scan optimistically, hoping that no flags, * minimum field width, or precision are specified. * Use the preallocated buffer, which is large enough * for all fast path cases. If the conversion specifier * is really more complex, run the loop a second time * using the slow path. * Note that fast path execution of %s bypasses the buffer * and %f is not attempted on the fast path due to * its large buffering requirements. */ op = percentPtr[1]; switch(op) { case 'l': case 'L': case 'h': sizeModifier = op; op = percentPtr[2]; fmtBuff[1] = sizeModifier; fmtBuff[2] = op; fmtBuff[3] = '\0'; specifierLength = 3; break; default: sizeModifier = ' '; fmtBuff[1] = op; fmtBuff[2] = '\0'; specifierLength = 2; break; } buffPtr = buff; buffLen = PRINTF_BUFFLEN; } else { /* * Slow path: Scan the conversion specifier and construct * a new format string, compute an upper bound on the * amount of buffering that sprintf will require, * and allocate a larger buffer if necessary. */ p = percentPtr + 1; fmtBuffPtr = &fmtBuff[1]; /* * Scan flags */ n = strspn(p, "-0+ #"); if(n > 5) goto ErrorReturn; CopyAndAdvance(&fmtBuffPtr, &p, n); /* Optimiser bug in SCO 5 - p is not advanced here under -O2. * -K noinline fixes it. Ben. */ /* * Scan minimum field width */ n = strspn(p, "0123456789"); if(n == 0) { if(*p == '*') { minWidth = va_arg(arg, int); if(abs(minWidth) > 999999) goto ErrorReturn; /* * The following use of strlen rather than the * value returned from sprintf is because SUNOS4 * returns a char * instead of an int count. */ sprintf(fmtBuffPtr, "%d", minWidth); fmtBuffPtr += strlen(fmtBuffPtr); p++; } else minWidth = 0; } else if(n <= 6) { minWidth = strtol(p, NULL, 10); CopyAndAdvance(&fmtBuffPtr, &p, n); } else goto ErrorReturn; /* * Scan precision */ if(*p == '.') { p++; n = strspn(p, "0123456789"); if(n == 0) { if(*p == '*') { precision = va_arg(arg, int); if(precision < 0) precision = 0; if(precision > 999999) goto ErrorReturn; /* * The following use of strlen rather than the * value returned from sprintf is because SUNOS4 * returns a char * instead of an int count. */ sprintf(fmtBuffPtr, ".%d", precision); fmtBuffPtr += strlen(fmtBuffPtr); p++; } else precision = 0; } else if(n <= 6) { precision = strtol(p, NULL, 10); *fmtBuffPtr++='.'; CopyAndAdvance(&fmtBuffPtr, &p, n); } else goto ErrorReturn; } else precision = -1; /* * Scan size modifier and conversion operation */ switch(*p) { case 'l': case 'L': case 'h': sizeModifier = *p; CopyAndAdvance(&fmtBuffPtr, &p, 1); break; default: sizeModifier = ' '; break; } op = *p; CopyAndAdvance(&fmtBuffPtr, &p, 1); ap_assert(fmtBuffPtr - fmtBuff < FMT_BUFFLEN); *fmtBuffPtr = '\0'; /* bwrite(bp,"[",1); bwrite(bp,fmtBuff,strlen(fmtBuff)); bwrite(bp,"]",1); */ specifierLength = p - percentPtr; /* * Bound the required buffer size. For s and f * conversions this requires examining the argument. */ switch(op) { case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'c': case 'p': buffReqd = max(precision, 46); break; case 's': charPtrArg = va_arg(arg, char *); if (charPtrArg == NULL) { charPtrArg = NULL_STRING; }; if(precision == -1) buffReqd = strlen(charPtrArg); else { p = memchr(charPtrArg, '\0', precision); if (p == NULL) buffReqd = precision; else buffReqd = p - charPtrArg; } break; case 'f': switch(sizeModifier) { case ' ': doubleArg = va_arg(arg, double); frexp(doubleArg, &expon); break; case 'L': lDoubleArg = va_arg(arg, LONG_DOUBLE); frexp(lDoubleArg, &expon); break; default: goto ErrorReturn; } if(precision == -1) precision = 6; buffReqd = precision + 3 + ((expon > 0) ? expon/3 : 0); break; case 'e': case 'E': case 'g': case 'G': if(precision == -1) precision = 6; buffReqd = precision + 8; break; case 'n': case '%': default: goto ErrorReturn; } buffReqd = max(buffReqd + 10, minWidth); /* * Allocate the buffer */ if(buffReqd <= PRINTF_BUFFLEN) { buffPtr = buff; buffLen = PRINTF_BUFFLEN; } else { if(auxBuffPtr == NULL || buffReqd > auxBuffLen) { if(auxBuffPtr != NULL) free(auxBuffPtr); auxBuffPtr = malloc(buffReqd); auxBuffLen = buffReqd; if(auxBuffPtr == NULL) goto ErrorReturn; } buffPtr = auxBuffPtr; buffLen = auxBuffLen; } } /* * This giant switch statement requires the following variables * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff. * When fastPath == FALSE and op == 's' or 'f', the argument * has been read into charPtrArg, doubleArg, or lDoubleArg. * The statement produces the boolean performedOp, TRUE iff * the op/sizeModifier were executed and argument consumed; * if performedOp, the characters written into buffPtr[] * and the character count buffCount (== EOF meaning error). * * The switch cases are arranged in the same order as in the * description of fprintf in section 15.11 of Harbison and Steele. */ performedOp = TRUE; switch(op) { case 'd': case 'i': switch(sizeModifier) { case ' ': intArg = va_arg(arg, int); sprintf(buffPtr, fmtBuff, intArg); buffCount = strlen(buffPtr); break; case 'l': longArg = va_arg(arg, long); sprintf(buffPtr, fmtBuff, longArg); buffCount = strlen(buffPtr); break; case 'h': shortArg = va_arg(arg, short); sprintf(buffPtr, fmtBuff, shortArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'u': case 'o': case 'x': case 'X': switch(sizeModifier) { case ' ': unsignedArg = va_arg(arg, unsigned); sprintf(buffPtr, fmtBuff, unsignedArg); buffCount = strlen(buffPtr); break; case 'l': uLongArg = va_arg(arg, unsigned long); sprintf(buffPtr, fmtBuff, uLongArg); buffCount = strlen(buffPtr); break; case 'h': uShortArg = va_arg(arg, unsigned short); sprintf(buffPtr, fmtBuff, uShortArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'c': switch(sizeModifier) { case ' ': intArg = va_arg(arg, int); sprintf(buffPtr, fmtBuff, intArg); buffCount = strlen(buffPtr); break; case 'l': /* * XXX: Allowed by ISO C Amendment 1, but * many platforms don't yet support wint_t */ goto ErrorReturn; default: goto ErrorReturn; } break; case 's': switch(sizeModifier) { case ' ': if(fastPath) { buffPtr = va_arg(arg, char *); if (buffPtr == NULL) { buffPtr = NULL_STRING; }; buffCount = strlen(buffPtr); buffLen = buffCount + 1; } else { sprintf(buffPtr, fmtBuff, charPtrArg); buffCount = strlen(buffPtr); } break; case 'l': /* * XXX: Don't know how to convert a sequence * of wide characters into a byte stream, or * even how to predict the buffering required. */ goto ErrorReturn; default: goto ErrorReturn; } break; case 'p': if(sizeModifier != ' ') goto ErrorReturn; voidPtrArg = va_arg(arg, void *); sprintf(buffPtr, fmtBuff, voidPtrArg); buffCount = strlen(buffPtr); break; case 'n': switch(sizeModifier) { case ' ': intPtrArg = va_arg(arg, int *); *intPtrArg = streamCount; break; case 'l': longPtrArg = va_arg(arg, long *); *longPtrArg = streamCount; break; case 'h': shortPtrArg = va_arg(arg, short *); *shortPtrArg = streamCount; break; default: goto ErrorReturn; } buffCount = 0; break; case 'f': if(fastPath) { performedOp = FALSE; break; } switch(sizeModifier) { case ' ': sprintf(buffPtr, fmtBuff, doubleArg); buffCount = strlen(buffPtr); break; case 'L': sprintf(buffPtr, fmtBuff, lDoubleArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; /* FIXME: Used to flow through here? Should it? Ben */ case 'e': case 'E': case 'g': case 'G': switch(sizeModifier) { case ' ': doubleArg = va_arg(arg, double); sprintf(buffPtr, fmtBuff, doubleArg); buffCount = strlen(buffPtr); break; case 'L': lDoubleArg = va_arg(arg, LONG_DOUBLE); sprintf(buffPtr, fmtBuff, lDoubleArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case '%': if(sizeModifier != ' ') goto ErrorReturn; buff[0] = '%'; buffCount = 1; break; case '\0': goto ErrorReturn; default: performedOp = FALSE; break; } /* switch(op) */ if(performedOp) break; if(!fastPath) goto ErrorReturn; fastPath = FALSE; } /* for (;;) */ ap_assert(buffCount < buffLen); if(buffCount > 0) { if(bwrite(bp,buffPtr,buffCount) < 0) goto ErrorReturn; streamCount += buffCount; } else if(buffCount < 0) goto ErrorReturn; f += specifierLength; } /* while(f != fStop) */ goto NormalReturn; ErrorReturn: streamCount = -1; NormalReturn: if(auxBuffPtr != NULL) free(auxBuffPtr); return streamCount; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.