int doprnt (char *buffer, register int bufsize, const char *format, const char *format_end, va_list ap) { const char *fmt = format; /* Pointer into format string */ register char *bufptr = buffer; /* Pointer into output buffer.. */ /* Use this for sprintf unless we need something really big. */ char tembuf[DBL_MAX_10_EXP + 100]; /* Size of sprintf_buffer. */ unsigned size_allocated = sizeof (tembuf); /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */ char *sprintf_buffer = tembuf; /* Buffer we have got with malloc. */ char *big_buffer = 0; register int tem; unsigned char *string; char fixed_buffer[20]; /* Default buffer for small formatting. */ char *fmtcpy; int minlen; unsigned char charbuf[MAX_MULTIBYTE_LENGTH + 1]; /* Used for %c. */ if (format_end == 0) format_end = format + strlen (format); if ((format_end - format + 1) < sizeof (fixed_buffer)) fmtcpy = fixed_buffer; else fmtcpy = (char *) alloca (format_end - format + 1); bufsize--; /* Loop until end of format string or buffer full. */ while (fmt != format_end && bufsize > 0) { if (*fmt == '%') /* Check for a '%' character */ { unsigned size_bound = 0; int width; /* Columns occupied by STRING. */ fmt++; /* Copy this one %-spec into fmtcpy. */ string = (unsigned char *) fmtcpy; *string++ = '%'; while (1) { *string++ = *fmt; if ('0' <= *fmt && *fmt <= '9') { /* Get an idea of how much space we might need. This might be a field width or a precision; e.g. %1.1000f and %1000.1f both might need 1000+ bytes. Parse the width or precision, checking for overflow. */ unsigned n = *fmt - '0'; while ('0' <= fmt[1] && fmt[1] <= '9') { if (n * 10 + fmt[1] - '0' < n) error ("Format width or precision too large"); n = n * 10 + fmt[1] - '0'; *string++ = *++fmt; } if (size_bound < n) size_bound = n; } else if (*fmt == '-' || *fmt == ' ' || *fmt == '.' || *fmt == '+') ; else break; fmt++; } *string = 0; /* Make the size bound large enough to handle floating point formats with large numbers. */ if (size_bound + DBL_MAX_10_EXP + 50 < size_bound) error ("Format width or precision too large"); size_bound += DBL_MAX_10_EXP + 50; /* Make sure we have that much. */ if (size_bound > size_allocated) { if (big_buffer) big_buffer = (char *) xrealloc (big_buffer, size_bound); else big_buffer = (char *) xmalloc (size_bound); sprintf_buffer = big_buffer; size_allocated = size_bound; } minlen = 0; switch (*fmt++) { default: error ("Invalid format operation %%%c", fmt[-1]); /* case 'b': */ case 'd': case 'o': case 'x': if (sizeof (int) == sizeof (EMACS_INT)) ; else if (sizeof (long) == sizeof (EMACS_INT)) /* Insert an `l' the right place. */ string[1] = string[0], string[0] = string[-1], string[-1] = 'l', string++; else abort (); sprintf (sprintf_buffer, fmtcpy, va_arg(ap, char *)); /* Now copy into final output, truncating as nec. */ string = (unsigned char *) sprintf_buffer; goto doit; case 'f': case 'e': case 'g': { double d = va_arg(ap, double); sprintf (sprintf_buffer, fmtcpy, d); /* Now copy into final output, truncating as nec. */ string = (unsigned char *) sprintf_buffer; goto doit; } case 'S': string[-1] = 's'; case 's': if (fmtcpy[1] != 's') minlen = atoi (&fmtcpy[1]); string = va_arg(ap, unsigned char *); tem = strlen (string); width = strwidth (string, tem); goto doit1; /* Copy string into final output, truncating if no room. */ doit: /* Coming here means STRING contains ASCII only. */ width = tem = strlen (string); doit1: /* We have already calculated: TEM -- length of STRING, WIDTH -- columns occupied by STRING when displayed, and MINLEN -- minimum columns of the output. */ if (minlen > 0) { while (minlen > width && bufsize > 0) { *bufptr++ = ' '; bufsize--; minlen--; } minlen = 0; } if (tem > bufsize) { /* Truncate the string at character boundary. */ tem = bufsize; while (!CHAR_HEAD_P (string[tem - 1])) tem--; memcpy (bufptr, string, tem); /* We must calculate WIDTH again. */ width = strwidth (bufptr, tem); } else memcpy (bufptr, string, tem); bufptr += tem; bufsize -= tem; if (minlen < 0) { while (minlen < - width && bufsize > 0) { *bufptr++ = ' '; bufsize--; minlen++; } minlen = 0; } continue; case 'c': { /* Sometimes for %c we pass a char, which would widen to int. Sometimes we pass XFASTINT() or XINT() values, which would be EMACS_INT. Let's hope that both are passed the same way, otherwise we'll need to rewrite callers. */ EMACS_INT chr = va_arg(ap, EMACS_INT); tem = CHAR_STRING ((int) chr, charbuf); string = charbuf; string[tem] = 0; width = strwidth (string, tem); if (fmtcpy[1] != 'c') minlen = atoi (&fmtcpy[1]); goto doit1; } case '%': fmt--; /* Drop thru and this % will be treated as normal */ } } { /* Just some character; Copy it if the whole multi-byte form fit in the buffer. */ char *save_bufptr = bufptr; do { *bufptr++ = *fmt++; } while (--bufsize > 0 && !CHAR_HEAD_P (*fmt)); if (!CHAR_HEAD_P (*fmt)) { bufptr = save_bufptr; break; } } };
ptrdiff_t doprnt (char *buffer, ptrdiff_t bufsize, const char *format, const char *format_end, va_list ap) { const char *fmt = format; /* Pointer into format string. */ char *bufptr = buffer; /* Pointer into output buffer. */ /* Use this for sprintf unless we need something really big. */ char tembuf[DBL_MAX_10_EXP + 100]; /* Size of sprintf_buffer. */ ptrdiff_t size_allocated = sizeof (tembuf); /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */ char *sprintf_buffer = tembuf; /* Buffer we have got with malloc. */ char *big_buffer = NULL; enum text_quoting_style quoting_style = text_quoting_style (); ptrdiff_t tem = -1; char *string; char fixed_buffer[20]; /* Default buffer for small formatting. */ char *fmtcpy; int minlen; char charbuf[MAX_MULTIBYTE_LENGTH + 1]; /* Used for %c. */ USE_SAFE_ALLOCA; if (format_end == 0) format_end = format + strlen (format); fmtcpy = (format_end - format < sizeof (fixed_buffer) - 1 ? fixed_buffer : SAFE_ALLOCA (format_end - format + 1)); bufsize--; /* Loop until end of format string or buffer full. */ while (fmt < format_end && bufsize > 0) { char const *fmt0 = fmt; char fmtchar = *fmt++; if (fmtchar == '%') { ptrdiff_t size_bound = 0; ptrdiff_t width; /* Columns occupied by STRING on display. */ enum { pDlen = sizeof pD - 1, pIlen = sizeof pI - 1, pMlen = sizeof pMd - 2 }; enum { no_modifier, long_modifier, pD_modifier, pI_modifier, pM_modifier } length_modifier = no_modifier; static char const modifier_len[] = { 0, 1, pDlen, pIlen, pMlen }; int maxmlen = max (max (1, pDlen), max (pIlen, pMlen)); int mlen; /* Copy this one %-spec into fmtcpy. */ string = fmtcpy; *string++ = '%'; while (fmt < format_end) { *string++ = *fmt; if ('0' <= *fmt && *fmt <= '9') { /* Get an idea of how much space we might need. This might be a field width or a precision; e.g. %1.1000f and %1000.1f both might need 1000+ bytes. Parse the width or precision, checking for overflow. */ ptrdiff_t n = *fmt - '0'; while (fmt + 1 < format_end && '0' <= fmt[1] && fmt[1] <= '9') { /* Avoid ptrdiff_t, size_t, and int overflow, as many sprintfs mishandle widths greater than INT_MAX. This test is simple but slightly conservative: e.g., (INT_MAX - INT_MAX % 10) is reported as an overflow even when it's not. */ if (n >= min (INT_MAX, min (PTRDIFF_MAX, SIZE_MAX)) / 10) error ("Format width or precision too large"); n = n * 10 + fmt[1] - '0'; *string++ = *++fmt; } if (size_bound < n) size_bound = n; } else if (! (*fmt == '-' || *fmt == ' ' || *fmt == '.' || *fmt == '+')) break; fmt++; } /* Check for the length modifiers in textual length order, so that longer modifiers override shorter ones. */ for (mlen = 1; mlen <= maxmlen; mlen++) { if (format_end - fmt < mlen) break; if (mlen == 1 && *fmt == 'l') length_modifier = long_modifier; if (mlen == pDlen && memcmp (fmt, pD, pDlen) == 0) length_modifier = pD_modifier; if (mlen == pIlen && memcmp (fmt, pI, pIlen) == 0) length_modifier = pI_modifier; if (mlen == pMlen && memcmp (fmt, pMd, pMlen) == 0) length_modifier = pM_modifier; } mlen = modifier_len[length_modifier]; memcpy (string, fmt + 1, mlen); string += mlen; fmt += mlen; *string = 0; /* Make the size bound large enough to handle floating point formats with large numbers. */ if (size_bound > min (PTRDIFF_MAX, SIZE_MAX) - DBL_MAX_10_EXP - 50) error ("Format width or precision too large"); size_bound += DBL_MAX_10_EXP + 50; /* Make sure we have that much. */ if (size_bound > size_allocated) { if (big_buffer) xfree (big_buffer); big_buffer = xmalloc (size_bound); sprintf_buffer = big_buffer; size_allocated = size_bound; } minlen = 0; switch (*fmt++) { default: error ("Invalid format operation %s", fmtcpy); /* case 'b': */ case 'l': case 'd': switch (length_modifier) { case no_modifier: { int v = va_arg (ap, int); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case long_modifier: { long v = va_arg (ap, long); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case pD_modifier: signed_pD_modifier: { ptrdiff_t v = va_arg (ap, ptrdiff_t); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case pI_modifier: { EMACS_INT v = va_arg (ap, EMACS_INT); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case pM_modifier: { intmax_t v = va_arg (ap, intmax_t); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; } /* Now copy into final output, truncating as necessary. */ string = sprintf_buffer; goto doit; case 'o': case 'x': switch (length_modifier) { case no_modifier: { unsigned v = va_arg (ap, unsigned); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case long_modifier: { unsigned long v = va_arg (ap, unsigned long); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case pD_modifier: goto signed_pD_modifier; case pI_modifier: { EMACS_UINT v = va_arg (ap, EMACS_UINT); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; case pM_modifier: { uintmax_t v = va_arg (ap, uintmax_t); tem = sprintf (sprintf_buffer, fmtcpy, v); } break; } /* Now copy into final output, truncating as necessary. */ string = sprintf_buffer; goto doit; case 'f': case 'e': case 'g': { double d = va_arg (ap, double); tem = sprintf (sprintf_buffer, fmtcpy, d); /* Now copy into final output, truncating as necessary. */ string = sprintf_buffer; goto doit; } case 'S': string[-1] = 's'; case 's': if (fmtcpy[1] != 's') minlen = atoi (&fmtcpy[1]); string = va_arg (ap, char *); tem = strlen (string); if (STRING_BYTES_BOUND < tem) error ("String for %%s or %%S format is too long"); width = strwidth (string, tem); goto doit1; /* Copy string into final output, truncating if no room. */ doit: eassert (0 <= tem); /* Coming here means STRING contains ASCII only. */ if (STRING_BYTES_BOUND < tem) error ("Format width or precision too large"); width = tem; doit1: /* We have already calculated: TEM -- length of STRING, WIDTH -- columns occupied by STRING when displayed, and MINLEN -- minimum columns of the output. */ if (minlen > 0) { while (minlen > width && bufsize > 0) { *bufptr++ = ' '; bufsize--; minlen--; } minlen = 0; } if (tem > bufsize) { /* Truncate the string at character boundary. */ tem = bufsize; do { tem--; if (CHAR_HEAD_P (string[tem])) { if (BYTES_BY_CHAR_HEAD (string[tem]) <= bufsize - tem) tem = bufsize; break; } } while (tem != 0); memcpy (bufptr, string, tem); bufptr[tem] = 0; /* Trigger exit from the loop, but make sure we return to the caller a value which will indicate that the buffer was too small. */ bufptr += bufsize; bufsize = 0; continue; } memcpy (bufptr, string, tem); bufptr += tem; bufsize -= tem; if (minlen < 0) { while (minlen < - width && bufsize > 0) { *bufptr++ = ' '; bufsize--; minlen++; } minlen = 0; } continue; case 'c': { int chr = va_arg (ap, int); tem = CHAR_STRING (chr, (unsigned char *) charbuf); string = charbuf; string[tem] = 0; width = strwidth (string, tem); if (fmtcpy[1] != 'c') minlen = atoi (&fmtcpy[1]); goto doit1; } case '%': fmt--; /* Drop thru and this % will be treated as normal */ } } char const *src; ptrdiff_t srclen; if (quoting_style == CURVE_QUOTING_STYLE && fmtchar == '`') src = uLSQM, srclen = sizeof uLSQM - 1; else if (quoting_style == CURVE_QUOTING_STYLE && fmtchar == '\'') src = uRSQM, srclen = sizeof uRSQM - 1; else if (quoting_style == STRAIGHT_QUOTING_STYLE && fmtchar == '`') src = "'", srclen = 1; else { while (fmt < format_end && !CHAR_HEAD_P (*fmt)) fmt++; src = fmt0, srclen = fmt - fmt0; } if (bufsize < srclen) { /* Truncate, but return value that will signal to caller that the buffer was too small. */ do *bufptr++ = '\0'; while (--bufsize != 0); } else { do *bufptr++ = *src++; while (--srclen != 0); } }