From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.bugs Subject: bug#43439: [PATCH] doprnt improvements Date: Thu, 15 Oct 2020 10:58:49 -0700 Organization: UCLA Computer Science Department Message-ID: References: <20200916015051.20517-1-eggert@cs.ucla.edu> <83o8m57oq7.fsf@gnu.org> <31fb34a7-26c3-552d-e8d4-74624455ffcb@cs.ucla.edu> <838sd75yor.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------07474DA9A9758F7BDC2793DE" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="34113"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 Cc: 43439@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Oct 15 20:00:48 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kT7YV-0008lf-Rz for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 15 Oct 2020 20:00:48 +0200 Original-Received: from localhost ([::1]:48202 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kT7YU-0005RC-Cs for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 15 Oct 2020 14:00:46 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:59746) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kT7Xm-0005R0-RI for bug-gnu-emacs@gnu.org; Thu, 15 Oct 2020 14:00:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:44957) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kT7Xm-0001Qn-G3 for bug-gnu-emacs@gnu.org; Thu, 15 Oct 2020 14:00:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kT7Xm-0004fk-DL for bug-gnu-emacs@gnu.org; Thu, 15 Oct 2020 14:00:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Paul Eggert Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 15 Oct 2020 18:00:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 43439 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 43439-submit@debbugs.gnu.org id=B43439.160278474217861 (code B ref 43439); Thu, 15 Oct 2020 18:00:02 +0000 Original-Received: (at 43439) by debbugs.gnu.org; 15 Oct 2020 17:59:02 +0000 Original-Received: from localhost ([127.0.0.1]:56503 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kT7Wn-0004dh-Ac for submit@debbugs.gnu.org; Thu, 15 Oct 2020 13:59:02 -0400 Original-Received: from zimbra.cs.ucla.edu ([131.179.128.68]:60084) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kT7Wl-0004dU-5w for 43439@debbugs.gnu.org; Thu, 15 Oct 2020 13:59:00 -0400 Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id CB2761600ED; Thu, 15 Oct 2020 10:58:52 -0700 (PDT) Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id wNCf-fXbiplT; Thu, 15 Oct 2020 10:58:50 -0700 (PDT) Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 73B541600F9; Thu, 15 Oct 2020 10:58:50 -0700 (PDT) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id NyX__Dq8Ioli; Thu, 15 Oct 2020 10:58:50 -0700 (PDT) Original-Received: from [192.168.1.9] (cpe-23-243-218-95.socal.res.rr.com [23.243.218.95]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id 3E7271600ED; Thu, 15 Oct 2020 10:58:50 -0700 (PDT) Autocrypt: addr=eggert@cs.ucla.edu; prefer-encrypt=mutual; keydata= mQINBEyAcmQBEADAAyH2xoTu7ppG5D3a8FMZEon74dCvc4+q1XA2J2tBy2pwaTqfhpxxdGA9 Jj50UJ3PD4bSUEgN8tLZ0san47l5XTAFLi2456ciSl5m8sKaHlGdt9XmAAtmXqeZVIYX/UFS 96fDzf4xhEmm/y7LbYEPQdUdxu47xA5KhTYp5bltF3WYDz1Ygd7gx07Auwp7iw7eNvnoDTAl KAl8KYDZzbDNCQGEbpY3efZIvPdeI+FWQN4W+kghy+P6au6PrIIhYraeua7XDdb2LS1en3Ss mE3QjqfRqI/A2ue8JMwsvXe/WK38Ezs6x74iTaqI3AFH6ilAhDqpMnd/msSESNFt76DiO1ZK QMr9amVPknjfPmJISqdhgB1DlEdw34sROf6V8mZw0xfqT6PKE46LcFefzs0kbg4GORf8vjG2 Sf1tk5eU8MBiyN/bZ03bKNjNYMpODDQQwuP84kYLkX2wBxxMAhBxwbDVZudzxDZJ1C2VXujC OJVxq2kljBM9ETYuUGqd75AW2LXrLw6+MuIsHFAYAgRr7+KcwDgBAfwhPBYX34nSSiHlmLC+ KaHLeCLF5ZI2vKm3HEeCTtlOg7xZEONgwzL+fdKo+D6SoC8RRxJKs8a3sVfI4t6CnrQzvJbB n6gxdgCu5i29J1QCYrCYvql2UyFPAK+do99/1jOXT4m2836j1wARAQABtCBQYXVsIEVnZ2Vy dCA8ZWdnZXJ0QGNzLnVjbGEuZWR1PokCVQQTAQgAPwIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC HgECF4AWIQR+N5Kp2 In-Reply-To: <838sd75yor.fsf@gnu.org> Content-Language: en-US X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:190618 Archived-At: This is a multi-part message in MIME format. --------------07474DA9A9758F7BDC2793DE Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit On 9/18/20 12:30 AM, Eli Zaretskii wrote: > How about ... we modify doprint to exit when either it finds > NUL or reaches the character specified by FORMAT_END? This will allow > us to keep some of the feature, and I think the amount of changes will > be smaller. It should also not be much slower than what you propose. Better yet, let's leave doprnt's API unchanged, and add a function evsnprintf (named by analogy from esprintf) whose API is like C vsnprintf but which does formatting the Emacs way. We can avoid duplication of code by implementing doprnt in terms of evsnprintf. This fixes the performance issue with current Emacs, and avoids the need for evsnprintf having to check for both NULs and FORMAT_END etc. Updated patch attached. --------------07474DA9A9758F7BDC2793DE Content-Type: text/x-patch; charset=UTF-8; name="0001-New-function-evsnprintf-to-speed-clean-up-doprnt.patch" Content-Disposition: attachment; filename*0="0001-New-function-evsnprintf-to-speed-clean-up-doprnt.patch" Content-Transfer-Encoding: quoted-printable >From c072575e7936c6e9f30864733677f19b1dcdc31a Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 15 Oct 2020 10:47:10 -0700 Subject: [PATCH] New function evsnprintf to speed/clean up doprnt MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit The new low-level C function evsnprintf improves on doprnt=E2=80=99s performance and internal checking. On my platform, this improved CPU speed of =E2=80=98make -C lisp compile-always=E2=80=99 by 6%= . This patch implements some of my suggestions in Bug#8545, with further changes suggested by Eli Zaretskii (Bug#43439). * src/doprnt.c: Improve comments. (SIZE_BOUND_EXTRA): Now at top level, for parse_format_integer. (parse_format_integer): New static function, containing some of the old doprnt. Fix a bug that caused doprnt to infloop on formats like "%10s" that Emacs does not use. We could simplify doprnt further if we dropped support for these never-used formats. (doprnt): Implement in terms of the new evsnprintf function. Put inside #if 0 since it=E2=80=99s not currently used. (evsnprintf): New function, that takes just one pass over FORMAT, not two. All doprnt callers changed to use evsnprintf. This new function has the contents of the old doprnt implementation, except its format argument argument is a null-terminated string. Assume C99 to make code clearer. Do not use malloc or alloca to allocate a copy of the format FMTCPY; instead, use a small fixed-size array FMTSTAR, and use '*' in that array to represent width and precision, passing them as separate int arguments. Use eassume to pacify GCC in switch statements. Drop support for "%S" which is not needed, is never used and which would cause GCC to warn anyway. --- src/doprnt.c | 260 ++++++++++++++++++++++++++++++--------------------- src/image.c | 2 +- src/lisp.h | 4 +- src/sysdep.c | 2 +- src/xdisp.c | 5 +- 5 files changed, 159 insertions(+), 114 deletions(-) diff --git a/src/doprnt.c b/src/doprnt.c index ceadf3bdfa..022b9ef16b 100644 --- a/src/doprnt.c +++ b/src/doprnt.c @@ -28,6 +28,7 @@ . For %s and %c, when field width is specified (e.g., %25s), it accou= nts for the display width of each character, according to char-width-table.= That is, it does not assume that each character takes one column on disp= lay. + Nor does it assume that each character is a single byte. =20 . If the size of the buffer is not enough to produce the formatted st= ring in its entirety, it makes sure that truncation does not chop the last @@ -42,38 +43,41 @@ Emacs can handle. =20 OTOH, this function supports only a small subset of the standard C fo= rmatted - output facilities. E.g., %u and %ll are not supported, and precision= is - ignored %s and %c conversions. (See below for the detailed documenta= tion of - what is supported.) However, this is okay, as this function is suppo= sed to - be called from `error' and similar functions, and thus does not need = to - support features beyond those in `Fformat_message', which is used - by `error' on the Lisp level. */ + output facilities. E.g., %u is not supported, precision is ignored + in %s and %c conversions, and %lld does not necessarily work and + code should use something like %"pM"d with intmax_t instead. + (See below for the detailed documentation of what is supported.) + However, this is okay, as this function is supposed to be called + from 'error' and similar C functions, and thus does not need to + support all the features of 'Fformat_message', which is used by the + Lisp 'error' function. */ =20 /* In the FORMAT argument this function supports ` and ' as directives that output left and right quotes as per =E2=80=98text-quoting style=E2= =80=99. It also supports the following %-sequences: =20 %s means print a string argument. - %S is treated as %s, for loose compatibility with `Fformat_message'. %d means print a `signed int' argument in decimal. %o means print an `unsigned int' argument in octal. %x means print an `unsigned int' argument in hex. %e means print a `double' argument in exponential notation. %f means print a `double' argument in decimal-point notation. %g means print a `double' argument in exponential notation - or in decimal-point notation, whichever uses fewer characters. + or in decimal-point notation, depending on the value; + this is often (though not always) the shorter of the two notations= . %c means print a `signed int' argument as a single character. %% means produce a literal % character. =20 - A %-sequence may contain optional flag, width, and precision specifie= rs, and - a length modifier, as follows: + A %-sequence other than %% may contain optional flags, width, precisi= on, + and length, as follows: =20 %character =20 where flags is [+ -0], width is [0-9]+, precision is .[0-9]+, and len= gth is empty or l or the value of the pD or pI or PRIdMAX (sans "d") macr= os. - Also, %% in a format stands for a single % in the output. A % that - does not introduce a valid %-sequence causes undefined behavior. + A % that does not introduce a valid %-sequence causes undefined behav= ior. + ASCII bytes in FORMAT other than % are copied through as-is; + non-ASCII bytes should not appear in FORMAT. =20 The + flag character inserts a + before any positive number, while a = space inserts a space before any positive number; these flags only affect %= d, %o, @@ -99,7 +103,9 @@ =20 For %e, %f, and %g sequences, the number after the "." in the precisi= on specifier says how many decimal places to show; if zero, the decimal = point - itself is omitted. For %s and %S, the precision specifier is ignored= . */ + itself is omitted. For %d, %o, and %x sequences, the precision speci= fies + the minimum number of digits to appear. Precision specifiers are + not supported for other %-sequences. */ =20 #include #include @@ -115,6 +121,36 @@ another macro. */ #include "character.h" =20 +/* Enough to handle floating point formats with large numbers. */ +enum { SIZE_BOUND_EXTRA =3D DBL_MAX_10_EXP + 50 }; + +/* Parse FMT as an unsigned decimal integer, putting its value into *VAL= UE. + Return the address of the first byte after the integer. + If FMT is not an integer, return FMT and store zero into *VALUE. */ +static char const * +parse_format_integer (char const *fmt, int *value) +{ + int n =3D 0; + bool overflow =3D false; + for (; '0' <=3D *fmt && *fmt <=3D '9'; fmt++) + { + overflow |=3D INT_MULTIPLY_WRAPV (n, 10, &n); + overflow |=3D INT_ADD_WRAPV (n, *fmt - '0', &n); + } + if (overflow || min (PTRDIFF_MAX, SIZE_MAX) - SIZE_BOUND_EXTRA < n) + error ("Format width or precision too large"); + *value =3D n; + return fmt; +} + +#if 0 +/* The doprnt function is not currently used in Emacs. + It's here in case it's needed later. + It acts like evsnprintf, except it also supports NULs in formats. */ + +ptrdiff_t doprnt (char *, ptrdiff_t, char const *, char const *, va_list= ) + ATTRIBUTE_FORMAT_PRINTF (3, 0); + /* Generate output from a format-spec FORMAT, terminated at position FORMAT_END. (*FORMAT_END is not part of the format, but must exist and be readabl= e.) @@ -130,13 +166,44 @@ ptrdiff_t doprnt (char *buffer, ptrdiff_t bufsize, const char *format, const char *format_end, va_list ap) +{ + ptrdiff_t nbytes =3D 0; + char const *f =3D format; + for (char const *fend; (fend =3D memchr (f, 0, format_end - f)); f =3D= fend + 1) + { + nbytes +=3D evsnprintf (buffer + nbytes, bufsize - nbytes, f, ap); + if (nbytes =3D=3D bufsize - 1) + return nbytes; + nbytes++; + } + + USE_SAFE_ALLOCA; + ptrdiff_t ftaillen =3D format_end - f; + char *ftail =3D SAFE_ALLOCA (ftaillen + 1); + memcpy (ftail, f, ftaillen); + ftail[ftaillen] =3D 0; + nbytes +=3D evsnprintf (buffer + nbytes, bufsize - nbytes, ftail, ap); + SAFE_FREE (); + return nbytes; +} +#endif + +/* Generate output from a format-spec FORMAT. + Output goes in BUFFER, which has room for BUFSIZE chars. + BUFSIZE must be positive. If the output does not fit, truncate it + to fit and return BUFSIZE - 1; if this truncates a multibyte + sequence, store '\0' into the sequence's first byte. + Return the number of bytes stored into BUFFER, excluding + the terminating null byte. Output is always null-terminated. + String arguments are passed as C strings. + Integers are passed as C integers. */ + +ptrdiff_t +evsnprintf (char *buffer, ptrdiff_t bufsize, char const *format, va_list= ap) { const char *fmt =3D format; /* Pointer into format string. */ char *bufptr =3D buffer; /* Pointer into output buffer. */ =20 - /* Enough to handle floating point formats with large numbers. */ - enum { SIZE_BOUND_EXTRA =3D DBL_MAX_10_EXP + 50 }; - /* Use this for sprintf unless we need something really big. */ char tembuf[SIZE_BOUND_EXTRA + 50]; =20 @@ -150,103 +217,91 @@ doprnt (char *buffer, ptrdiff_t bufsize, const cha= r *format, char *big_buffer =3D NULL; =20 enum text_quoting_style quoting_style =3D text_quoting_style (); - ptrdiff_t tem =3D -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 =3D=3D 0) - format_end =3D format + strlen (format); - - fmtcpy =3D (format_end - format < sizeof (fixed_buffer) - 1 - ? fixed_buffer - : SAFE_ALLOCA (format_end - format + 1)); =20 bufsize--; =20 /* Loop until end of format string or buffer full. */ - while (fmt < format_end && bufsize > 0) + while (*fmt && bufsize > 0) { char const *fmt0 =3D fmt; char fmtchar =3D *fmt++; if (fmtchar =3D=3D '%') { - ptrdiff_t size_bound =3D 0; ptrdiff_t width; /* Columns occupied by STRING on display. */ enum { pDlen =3D sizeof pD - 1, pIlen =3D sizeof pI - 1, - pMlen =3D sizeof PRIdMAX - 2 + pMlen =3D sizeof PRIdMAX - 2, + maxmlen =3D max (max (1, pDlen), max (pIlen, pMlen)) }; enum { no_modifier, long_modifier, pD_modifier, pI_modifier, pM_modifier } length_modifier =3D no_modifier; static char const modifier_len[] =3D { 0, 1, pDlen, pIlen, pMlen }; - int maxmlen =3D max (max (1, pDlen), max (pIlen, pMlen)); int mlen; + char charbuf[MAX_MULTIBYTE_LENGTH + 1]; /* Used for %c. */ =20 - /* Copy this one %-spec into fmtcpy. */ - string =3D fmtcpy; + /* Width and precision specified by this %-sequence. */ + int wid =3D 0, prec =3D -1; + + /* FMTSTAR will be a "%*.*X"-like version of this %-sequence. + Start by putting '%' into FMTSTAR. */ + char fmtstar[sizeof "%-+ 0*.*d" + maxmlen]; + char *string =3D fmtstar; *string++ =3D '%'; - while (fmt < format_end) + + /* Copy at most one instance of each flag into FMTSTAR. */ + bool minusflag =3D false, plusflag =3D false, zeroflag =3D false, + spaceflag =3D false; + for (;; fmt++) { - *string++ =3D *fmt; - if ('0' <=3D *fmt && *fmt <=3D '9') + *string =3D *fmt; + switch (*fmt) { - /* 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. */ - int n =3D *fmt - '0'; - bool overflow =3D false; - while (fmt + 1 < format_end - && '0' <=3D fmt[1] && fmt[1] <=3D '9') - { - overflow |=3D INT_MULTIPLY_WRAPV (n, 10, &n); - overflow |=3D INT_ADD_WRAPV (n, fmt[1] - '0', &n); - *string++ =3D *++fmt; - } - - if (overflow - || min (PTRDIFF_MAX, SIZE_MAX) - SIZE_BOUND_EXTRA < n) - error ("Format width or precision too large"); - if (size_bound < n) - size_bound =3D n; + case '-': string +=3D !minusflag; minusflag =3D true; continue; + case '+': string +=3D !plusflag; plusflag =3D true; continue; + case ' ': string +=3D !spaceflag; spaceflag =3D true; continue; + case '0': string +=3D !zeroflag; zeroflag =3D true; continue; } - else if (! (*fmt =3D=3D '-' || *fmt =3D=3D ' ' || *fmt =3D=3D '.' - || *fmt =3D=3D '+')) - break; - fmt++; + break; } =20 + /* Parse width and precision, putting "*.*" into FMTSTAR. */ + if ('1' <=3D *fmt && *fmt <=3D '9') + fmt =3D parse_format_integer (fmt, &wid); + if (*fmt =3D=3D '.') + fmt =3D parse_format_integer (fmt + 1, &prec); + *string++ =3D '*'; + *string++ =3D '.'; + *string++ =3D '*'; + /* Check for the length modifiers in textual length order, so that longer modifiers override shorter ones. */ for (mlen =3D 1; mlen <=3D maxmlen; mlen++) { - if (format_end - fmt < mlen) - break; if (mlen =3D=3D 1 && *fmt =3D=3D 'l') length_modifier =3D long_modifier; - if (mlen =3D=3D pDlen && memcmp (fmt, pD, pDlen) =3D=3D 0) + if (mlen =3D=3D pDlen && strncmp (fmt, pD, pDlen) =3D=3D 0) length_modifier =3D pD_modifier; - if (mlen =3D=3D pIlen && memcmp (fmt, pI, pIlen) =3D=3D 0) + if (mlen =3D=3D pIlen && strncmp (fmt, pI, pIlen) =3D=3D 0) length_modifier =3D pI_modifier; - if (mlen =3D=3D pMlen && memcmp (fmt, PRIdMAX, pMlen) =3D=3D 0) + if (mlen =3D=3D pMlen && strncmp (fmt, PRIdMAX, pMlen) =3D=3D 0) length_modifier =3D pM_modifier; } =20 + /* Copy optional length modifier and conversion specifier + character into FMTSTAR, and append a NUL. */ mlen =3D modifier_len[length_modifier]; - memcpy (string, fmt + 1, mlen); - string +=3D mlen; + string =3D mempcpy (string, fmt, mlen + 1); fmt +=3D mlen; *string =3D 0; =20 - /* Make the size bound large enough to handle floating point formats + /* 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. + Make it large enough to handle floating point formats with large numbers. */ - size_bound +=3D SIZE_BOUND_EXTRA; + ptrdiff_t size_bound =3D max (wid, prec) + SIZE_BOUND_EXTRA; =20 /* Make sure we have that much. */ if (size_bound > size_allocated) @@ -257,48 +312,49 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, sprintf_buffer =3D big_buffer; size_allocated =3D size_bound; } - minlen =3D 0; + int minlen =3D 0; + ptrdiff_t tem; switch (*fmt++) { default: - error ("Invalid format operation %s", fmtcpy); + error ("Invalid format operation %s", fmt0); =20 -/* case 'b': */ - case 'l': case 'd': switch (length_modifier) { case no_modifier: { int v =3D va_arg (ap, int); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case long_modifier: { long v =3D va_arg (ap, long); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case pD_modifier: signed_pD_modifier: { ptrdiff_t v =3D va_arg (ap, ptrdiff_t); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case pI_modifier: { EMACS_INT v =3D va_arg (ap, EMACS_INT); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case pM_modifier: { intmax_t v =3D va_arg (ap, intmax_t); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; + default: + eassume (false); } /* Now copy into final output, truncating as necessary. */ string =3D sprintf_buffer; @@ -311,13 +367,13 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, case no_modifier: { unsigned v =3D va_arg (ap, unsigned); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case long_modifier: { unsigned long v =3D va_arg (ap, unsigned long); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case pD_modifier: @@ -325,15 +381,17 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, case pI_modifier: { EMACS_UINT v =3D va_arg (ap, EMACS_UINT); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; case pM_modifier: { uintmax_t v =3D va_arg (ap, uintmax_t); - tem =3D sprintf (sprintf_buffer, fmtcpy, v); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, v); } break; + default: + eassume (false); } /* Now copy into final output, truncating as necessary. */ string =3D sprintf_buffer; @@ -344,22 +402,18 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, case 'g': { double d =3D va_arg (ap, double); - tem =3D sprintf (sprintf_buffer, fmtcpy, d); + tem =3D sprintf (sprintf_buffer, fmtstar, wid, prec, d); /* Now copy into final output, truncating as necessary. */ string =3D sprintf_buffer; goto doit; } =20 - case 'S': - string[-1] =3D 's'; - FALLTHROUGH; case 's': - if (fmtcpy[1] !=3D 's') - minlen =3D atoi (&fmtcpy[1]); + minlen =3D minusflag ? -wid : wid; string =3D va_arg (ap, char *); tem =3D strnlen (string, STRING_BYTES_BOUND + 1); if (tem =3D=3D STRING_BYTES_BOUND + 1) - error ("String for %%s or %%S format is too long"); + error ("String for %%s format is too long"); width =3D strwidth (string, tem); goto doit1; =20 @@ -432,14 +486,12 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, string =3D charbuf; string[tem] =3D 0; width =3D strwidth (string, tem); - if (fmtcpy[1] !=3D 'c') - minlen =3D atoi (&fmtcpy[1]); + minlen =3D minusflag ? -wid : wid; goto doit1; } =20 case '%': /* Treat this '%' as normal. */ - fmt0 =3D fmt - 1; break; } } @@ -450,13 +502,13 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, src =3D uLSQM, srclen =3D sizeof uLSQM - 1; else if (quoting_style =3D=3D CURVE_QUOTING_STYLE && fmtchar =3D=3D= '\'') src =3D uRSQM, srclen =3D sizeof uRSQM - 1; - else if (quoting_style =3D=3D STRAIGHT_QUOTING_STYLE && fmtchar =3D= =3D '`') - src =3D "'", srclen =3D 1; else { - while (fmt < format_end && !CHAR_HEAD_P (*fmt)) - fmt++; - src =3D fmt0, srclen =3D fmt - fmt0; + if (quoting_style =3D=3D STRAIGHT_QUOTING_STYLE && fmtchar =3D=3D '`'= ) + fmtchar =3D '\''; + eassert (ASCII_CHAR_P (fmtchar)); + *bufptr++ =3D fmtchar; + continue; } =20 if (bufsize < srclen) @@ -479,15 +531,13 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char= *format, xfree (big_buffer); =20 *bufptr =3D 0; /* Make sure our string ends with a '\0' */ - - SAFE_FREE (); return bufptr - buffer; } =20 /* Format to an unbounded buffer BUF. This is like sprintf, except it is not limited to returning an 'int' so it doesn't have a silly 2 GiB limit on typical 64-bit hosts. However, it is limited to the - Emacs-style formats that doprnt supports, and it requotes ` and ' + Emacs-style formats that evsnprintf supports, and it requotes ` and ' as per =E2=80=98text-quoting-style=E2=80=99. =20 Return the number of bytes put into BUF, excluding the terminating @@ -495,10 +545,9 @@ doprnt (char *buffer, ptrdiff_t bufsize, const char = *format, ptrdiff_t esprintf (char *buf, char const *format, ...) { - ptrdiff_t nbytes; va_list ap; va_start (ap, format); - nbytes =3D doprnt (buf, TYPE_MAXIMUM (ptrdiff_t), format, 0, ap); + ptrdiff_t nbytes =3D evsnprintf (buf, TYPE_MAXIMUM (ptrdiff_t), format= , ap); va_end (ap); return nbytes; } @@ -534,10 +583,9 @@ evxprintf (char **buf, ptrdiff_t *bufsize, { for (;;) { - ptrdiff_t nbytes; va_list ap_copy; va_copy (ap_copy, ap); - nbytes =3D doprnt (*buf, *bufsize, format, 0, ap_copy); + ptrdiff_t nbytes =3D evsnprintf (*buf, *bufsize, format, ap_copy); va_end (ap_copy); if (nbytes < *bufsize - 1) return nbytes; diff --git a/src/image.c b/src/image.c index 25d5af8a8d..8a030c154a 100644 --- a/src/image.c +++ b/src/image.c @@ -7826,7 +7826,7 @@ tiff_handler (const char *, const char *, const cha= r *, va_list) tiff_handler (const char *log_format, const char *title, const char *format, va_list ap) { - /* doprnt is not suitable here, as TIFF handlers are called from + /* evsnprintf is not suitable here, as TIFF handlers are called from libtiff and are passed arbitrary printf directives. Instead, use vsnprintf, taking care to be portable to nonstandard environments where vsnprintf returns -1 on buffer overflow. Since it's just a diff --git a/src/lisp.h b/src/lisp.h index 45353fbef3..e4de9c8e9c 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4034,8 +4034,8 @@ #define FLOAT_TO_STRING_BUFSIZE 350 extern void syms_of_print (void); =20 /* Defined in doprnt.c. */ -extern ptrdiff_t doprnt (char *, ptrdiff_t, const char *, const char *, - va_list); +extern ptrdiff_t evsnprintf (char *, ptrdiff_t, const char *, va_list) + ATTRIBUTE_FORMAT_PRINTF (3, 0); extern ptrdiff_t esprintf (char *, char const *, ...) ATTRIBUTE_FORMAT_PRINTF (2, 3); extern ptrdiff_t exprintf (char **, ptrdiff_t *, char const *, ptrdiff_t= , diff --git a/src/sysdep.c b/src/sysdep.c index f6c0ddee01..da176634a5 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2192,7 +2192,7 @@ snprintf (char *buf, size_t bufsize, char const *fo= rmat, ...) if (size) { va_start (ap, format); - nbytes =3D doprnt (buf, size, format, 0, ap); + nbytes =3D evsnprintf (buf, size, format, ap); va_end (ap); } =20 diff --git a/src/xdisp.c b/src/xdisp.c index 5a62cd6eb5..2f1fe74e1d 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11269,13 +11269,10 @@ vmessage (const char *m, va_list ap) { if (m) { - ptrdiff_t len; ptrdiff_t maxsize =3D FRAME_MESSAGE_BUF_SIZE (f); USE_SAFE_ALLOCA; char *message_buf =3D SAFE_ALLOCA (maxsize + 1); - - len =3D doprnt (message_buf, maxsize, m, 0, ap); - + ptrdiff_t len =3D evsnprintf (message_buf, maxsize, m, ap); message3 (make_string (message_buf, len)); SAFE_FREE (); } --=20 2.25.1 --------------07474DA9A9758F7BDC2793DE--