From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.devel Subject: Re: i18n/l10n summary Date: Thu, 1 Jun 2017 16:20:55 -0700 Organization: UCLA Computer Science Department Message-ID: References: NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------843490A2616B420F0FE4CEAA" X-Trace: blaine.gmane.org 1496359280 6372 195.159.176.226 (1 Jun 2017 23:21:20 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 1 Jun 2017 23:21:20 +0000 (UTC) User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.1.0 To: Philipp Stephani , Jean-Christophe Helary , emacs-devel Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Jun 02 01:21:14 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dGZOz-0001Ik-MN for ged-emacs-devel@m.gmane.org; Fri, 02 Jun 2017 01:21:14 +0200 Original-Received: from localhost ([::1]:47061 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dGZP5-0006Yx-1r for ged-emacs-devel@m.gmane.org; Thu, 01 Jun 2017 19:21:19 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:46297) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dGZOq-0006Yo-OP for emacs-devel@gnu.org; Thu, 01 Jun 2017 19:21:08 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dGZOn-0006pT-2D for emacs-devel@gnu.org; Thu, 01 Jun 2017 19:21:04 -0400 Original-Received: from zimbra.cs.ucla.edu ([131.179.128.68]:49288) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dGZOm-0006kR-BJ for emacs-devel@gnu.org; Thu, 01 Jun 2017 19:21:00 -0400 Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id C791F1600D9; Thu, 1 Jun 2017 16:20:58 -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 7HeIz7151OBv; Thu, 1 Jun 2017 16:20:56 -0700 (PDT) Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 326281600DF; Thu, 1 Jun 2017 16:20:56 -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 elW8XSX7B28c; Thu, 1 Jun 2017 16:20:56 -0700 (PDT) Original-Received: from Penguin.CS.UCLA.EDU (Penguin.CS.UCLA.EDU [131.179.64.200]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id A7ED51600D9; Thu, 1 Jun 2017 16:20:55 -0700 (PDT) In-Reply-To: Content-Language: en-US X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 131.179.128.68 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:215387 Archived-At: This is a multi-part message in MIME format. --------------843490A2616B420F0FE4CEAA Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit On 06/01/2017 01:17 AM, Philipp Stephani wrote: > > - Probably there's a bug lurking because the info[n] ought to be > indexed by specification index, not argument index. Something like > (format "%1$c %1$d" ?a) will probably do the wrong thing (untested). Sorry, I'm not following. That call returns "a 97"; isn't that the expected result? > - We should ban mixing explicit and implicit field numbers, like POSIX > printf(3) does. The gain from allowing to mix is negligible, and it > makes the implementation and the documentation needlessly complex. Sounds good, and I installed the attached. The 1st patch fixes a performance regression introduced by calling strtoumax. I went whole-hog and removed all calls to strtoumax, since they're all performance-significant, plus it makes for one less porting issue to worry about. The 2nd patch fixes the documentation along the lines that you suggested. And on further thought, the tradition for Emacs is to document supported behavior and not worry about slowing Emacs down to check for undocumented usage (aside from preventing crashes), so with that in mind the 2nd patch removes the check for %0$ (which never crashes). --------------843490A2616B420F0FE4CEAA Content-Type: text/x-patch; name="0001-Improve-performance-by-avoiding-strtoumax.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="0001-Improve-performance-by-avoiding-strtoumax.patch" =46rom b126485b8ce3046ca47caaba2f845a39604dd76f Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 1 Jun 2017 16:03:12 -0700 Subject: [PATCH 1/2] Improve performance by avoiding strtoumax This made (string-to-number "10") 20% faster on my old desktop, an AMD Phenom II X4 910e running Fedora 25 x86-64. * admin/merge-gnulib (GNULIB_MODULES): Remove strtoumax. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * lib/strtoul.c, lib/strtoull.c, lib/strtoumax.c, m4/strtoull.m4: * m4/strtoumax.m4: Remove. * src/editfns.c (str2num): New function. (styled_format): Use it instead of strtoumax. Use ptrdiff_t instead of uintmax_t. Check for integer overflow. * src/lread.c (LEAD_INT, DOT_CHAR, TRAIL_INT, E_EXP): Move to private scope and make them enums. (string_to_number): Compute integer value directly during first pass instead of revisiting it with strtoumax later. --- admin/merge-gnulib | 2 +- lib/gnulib.mk.in | 27 +------------------------ lib/strtoul.c | 19 ------------------ lib/strtoull.c | 26 ------------------------ lib/strtoumax.c | 2 -- m4/gnulib-comp.m4 | 30 --------------------------- m4/strtoull.m4 | 24 ---------------------- m4/strtoumax.m4 | 28 -------------------------- src/editfns.c | 43 ++++++++++++++++++++++++++++----------- src/lread.c | 59 +++++++++++++++++++++++-------------------------= ------ 10 files changed, 58 insertions(+), 202 deletions(-) delete mode 100644 lib/strtoul.c delete mode 100644 lib/strtoull.c delete mode 100644 lib/strtoumax.c delete mode 100644 m4/strtoull.m4 delete mode 100644 m4/strtoumax.m4 diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 45e4a78..e5fb0f5 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -38,7 +38,7 @@ GNULIB_MODULES=3D manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio - stpcpy strftime strtoimax strtoumax symlink sys_stat + stpcpy strftime strtoimax symlink sys_stat sys_time time time_r time_rz timegm timer-time timespec-add timespec-s= ub update-copyright utimens vla warnings diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index d23c2a5..73d3043 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -21,7 +21,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --lib=3Dlibgnu --source-base=3Dlib = --m4-base=3Dm4 --doc-base=3Ddoc --tests-base=3Dtests --aux-dir=3Dbuild-au= x --avoid=3Dclose --avoid=3Ddup --avoid=3Dfchdir --avoid=3Dfstat --avoid=3D= malloc-posix --avoid=3Dmsvc-inval --avoid=3Dmsvc-nothrow --avoid=3Dopen -= -avoid=3Dopenat-die --avoid=3Dopendir --avoid=3Draise --avoid=3Dsave-cwd = --avoid=3Dselect --avoid=3Dsetenv --avoid=3Dsigprocmask --avoid=3Dstat --= avoid=3Dstdarg --avoid=3Dstdbool --avoid=3Dthreadlib --avoid=3Dtzset --av= oid=3Dunsetenv --avoid=3Dutime --avoid=3Dutime-h --gnu-make --makefile-na= me=3Dgnulib.mk.in --conditional-dependencies --no-libtool --macro-prefix=3D= gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadli= nkat close-stream count-leading-zeros count-one-bits count-trailing-zeros= crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec d= up2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode= filevercmp flexmember fstatat fsync getloadavg getopt-gnu gettime gettim= eofday gitlog-to-changelog ignore-value intprops largefile lstat manywarn= ings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-a= cl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stdde= f stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time tim= e time_r time_rz timegm timer-time timespec-add timespec-sub update-copyr= ight utimens vla warnings +# Reproduce by: gnulib-tool --import --lib=3Dlibgnu --source-base=3Dlib = --m4-base=3Dm4 --doc-base=3Ddoc --tests-base=3Dtests --aux-dir=3Dbuild-au= x --avoid=3Dclose --avoid=3Ddup --avoid=3Dfchdir --avoid=3Dfstat --avoid=3D= malloc-posix --avoid=3Dmsvc-inval --avoid=3Dmsvc-nothrow --avoid=3Dopen -= -avoid=3Dopenat-die --avoid=3Dopendir --avoid=3Draise --avoid=3Dsave-cwd = --avoid=3Dselect --avoid=3Dsetenv --avoid=3Dsigprocmask --avoid=3Dstat --= avoid=3Dstdarg --avoid=3Dstdbool --avoid=3Dthreadlib --avoid=3Dtzset --av= oid=3Dunsetenv --avoid=3Dutime --avoid=3Dutime-h --gnu-make --makefile-na= me=3Dgnulib.mk.in --conditional-dependencies --no-libtool --macro-prefix=3D= gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadli= nkat close-stream count-leading-zeros count-one-bits count-trailing-zeros= crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec d= up2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode= filevercmp flexmember fstatat fsync getloadavg getopt-gnu gettime gettim= eofday gitlog-to-changelog ignore-value intprops largefile lstat manywarn= ings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-a= cl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stdde= f stdio stpcpy strftime strtoimax symlink sys_stat sys_time time time_r t= ime_rz timegm timer-time timespec-add timespec-sub update-copyright utime= ns vla warnings =20 =20 MOSTLYCLEANFILES +=3D core *.stackdump @@ -905,7 +905,6 @@ gl_GNULIB_ENABLED_getdtablesize =3D @gl_GNULIB_ENABLE= D_getdtablesize@ gl_GNULIB_ENABLED_getgroups =3D @gl_GNULIB_ENABLED_getgroups@ gl_GNULIB_ENABLED_secure_getenv =3D @gl_GNULIB_ENABLED_secure_getenv@ gl_GNULIB_ENABLED_strtoll =3D @gl_GNULIB_ENABLED_strtoll@ -gl_GNULIB_ENABLED_strtoull =3D @gl_GNULIB_ENABLED_strtoull@ gl_GNULIB_ENABLED_tempname =3D @gl_GNULIB_ENABLED_tempname@ gl_LIBOBJS =3D @gl_LIBOBJS@ gl_LTLIBOBJS =3D @gl_LTLIBOBJS@ @@ -2507,30 +2506,6 @@ EXTRA_libgnu_a_SOURCES +=3D strtol.c strtoll.c endif ## end gnulib module strtoll =20 -## begin gnulib module strtoull -ifeq (,$(OMIT_GNULIB_MODULE_strtoull)) - -ifneq (,$(gl_GNULIB_ENABLED_strtoull)) - -endif -EXTRA_DIST +=3D strtol.c strtoul.c strtoull.c - -EXTRA_libgnu_a_SOURCES +=3D strtol.c strtoul.c strtoull.c - -endif -## end gnulib module strtoull - -## begin gnulib module strtoumax -ifeq (,$(OMIT_GNULIB_MODULE_strtoumax)) - - -EXTRA_DIST +=3D strtoimax.c strtoumax.c - -EXTRA_libgnu_a_SOURCES +=3D strtoimax.c strtoumax.c - -endif -## end gnulib module strtoumax - ## begin gnulib module symlink ifeq (,$(OMIT_GNULIB_MODULE_symlink)) =20 diff --git a/lib/strtoul.c b/lib/strtoul.c deleted file mode 100644 index c4974e0..0000000 --- a/lib/strtoul.c +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright (C) 1991, 1997, 2009-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - 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 .= */ - -#define UNSIGNED 1 - -#include "strtol.c" diff --git a/lib/strtoull.c b/lib/strtoull.c deleted file mode 100644 index 51ae3ac..0000000 --- a/lib/strtoull.c +++ /dev/null @@ -1,26 +0,0 @@ -/* Function to parse an 'unsigned long long int' from text. - Copyright (C) 1995-1997, 1999, 2009-2017 Free Software Foundation, In= c. - NOTE: The canonical source of this file is maintained with the GNU C - Library. Bugs can be reported to bug-glibc@gnu.org. - - 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 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 .= */ - -#define QUAD 1 - -#include "strtoul.c" - -#ifdef _LIBC -strong_alias (__strtoull_internal, __strtouq_internal) -weak_alias (strtoull, strtouq) -#endif diff --git a/lib/strtoumax.c b/lib/strtoumax.c deleted file mode 100644 index dc395d6..0000000 --- a/lib/strtoumax.c +++ /dev/null @@ -1,2 +0,0 @@ -#define UNSIGNED 1 -#include "strtoimax.c" diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 3f196d4..8f53a99 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -140,8 +140,6 @@ AC_DEFUN # Code from module string: # Code from module strtoimax: # Code from module strtoll: - # Code from module strtoull: - # Code from module strtoumax: # Code from module symlink: # Code from module sys_select: # Code from module sys_stat: @@ -364,12 +362,6 @@ AC_DEFUN gl_PREREQ_STRTOIMAX fi gl_INTTYPES_MODULE_INDICATOR([strtoimax]) - gl_FUNC_STRTOUMAX - if test $HAVE_DECL_STRTOUMAX =3D 0 || test $REPLACE_STRTOUMAX =3D 1; t= hen - AC_LIBOBJ([strtoumax]) - gl_PREREQ_STRTOUMAX - fi - gl_INTTYPES_MODULE_INDICATOR([strtoumax]) gl_FUNC_SYMLINK if test $HAVE_SYMLINK =3D 0 || test $REPLACE_SYMLINK =3D 1; then AC_LIBOBJ([symlink]) @@ -420,7 +412,6 @@ AC_DEFUN gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=3Dfalse gl_gnulib_enabled_secure_getenv=3Dfalse gl_gnulib_enabled_strtoll=3Dfalse - gl_gnulib_enabled_strtoull=3Dfalse gl_gnulib_enabled_tempname=3Dfalse gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=3Dfalse func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () @@ -569,18 +560,6 @@ AC_DEFUN gl_gnulib_enabled_strtoll=3Dtrue fi } - func_gl_gnulib_m4code_strtoull () - { - if ! $gl_gnulib_enabled_strtoull; then - gl_FUNC_STRTOULL - if test $HAVE_STRTOULL =3D 0; then - AC_LIBOBJ([strtoull]) - gl_PREREQ_STRTOULL - fi - gl_STDLIB_MODULE_INDICATOR([strtoull]) - gl_gnulib_enabled_strtoull=3Dtrue - fi - } func_gl_gnulib_m4code_tempname () { if ! $gl_gnulib_enabled_tempname; then @@ -649,9 +628,6 @@ AC_DEFUN if { test $HAVE_DECL_STRTOIMAX =3D 0 || test $REPLACE_STRTOIMAX =3D 1;= } && test $ac_cv_type_long_long_int =3D yes; then func_gl_gnulib_m4code_strtoll fi - if { test $HAVE_DECL_STRTOUMAX =3D 0 || test $REPLACE_STRTOUMAX =3D 1;= } && test $ac_cv_type_unsigned_long_long_int =3D yes; then - func_gl_gnulib_m4code_strtoull - fi if test $HAVE_TIMEGM =3D 0 || test $REPLACE_TIMEGM =3D 1; then func_gl_gnulib_m4code_5264294aa0a5557541b53c8c741f7f31 fi @@ -670,7 +646,6 @@ AC_DEFUN AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [= $gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_secure_getenv], [$gl_gnulib_enabled_= secure_getenv]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtol= l]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strto= ull]) AM_CONDITIONAL([gl_GNULIB_ENABLED_tempname], [$gl_gnulib_enabled_tempn= ame]) AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [= $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) # End of code from modules @@ -940,9 +915,6 @@ AC_DEFUN lib/strtoimax.c lib/strtol.c lib/strtoll.c - lib/strtoul.c - lib/strtoull.c - lib/strtoumax.c lib/symlink.c lib/sys_select.in.h lib/sys_stat.in.h @@ -1051,8 +1023,6 @@ AC_DEFUN m4/string_h.m4 m4/strtoimax.m4 m4/strtoll.m4 - m4/strtoull.m4 - m4/strtoumax.m4 m4/symlink.m4 m4/sys_select_h.m4 m4/sys_socket_h.m4 diff --git a/m4/strtoull.m4 b/m4/strtoull.m4 deleted file mode 100644 index c6b2150..0000000 --- a/m4/strtoull.m4 +++ /dev/null @@ -1,24 +0,0 @@ -# strtoull.m4 serial 7 -dnl Copyright (C) 2002, 2004, 2006, 2008-2017 Free Software Foundation, = Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_STRTOULL], -[ - AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) - dnl We don't need (and can't compile) the replacement strtoull - dnl unless the type 'unsigned long long int' exists. - AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) - if test "$ac_cv_type_unsigned_long_long_int" =3D yes; then - AC_CHECK_FUNCS([strtoull]) - if test $ac_cv_func_strtoull =3D no; then - HAVE_STRTOULL=3D0 - fi - fi -]) - -# Prerequisites of lib/strtoull.c. -AC_DEFUN([gl_PREREQ_STRTOULL], [ - : -]) diff --git a/m4/strtoumax.m4 b/m4/strtoumax.m4 deleted file mode 100644 index 43ef5b5..0000000 --- a/m4/strtoumax.m4 +++ /dev/null @@ -1,28 +0,0 @@ -# strtoumax.m4 serial 12 -dnl Copyright (C) 2002-2004, 2006, 2009-2017 Free Software Foundation, I= nc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_STRTOUMAX], -[ - AC_REQUIRE([gl_INTTYPES_H_DEFAULTS]) - - dnl On OSF/1 5.1 with cc, this function is declared but not defined. - AC_CHECK_FUNCS_ONCE([strtoumax]) - AC_CHECK_DECLS_ONCE([strtoumax]) - if test "$ac_cv_have_decl_strtoumax" =3D yes; then - if test "$ac_cv_func_strtoumax" !=3D yes; then - # HP-UX 11.11 has "#define strtoimax(...) ..." but no function. - REPLACE_STRTOUMAX=3D1 - fi - else - HAVE_DECL_STRTOUMAX=3D0 - fi -]) - -# Prerequisites of lib/strtoumax.c. -AC_DEFUN([gl_PREREQ_STRTOUMAX], [ - AC_CHECK_DECLS([strtoull]) - AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) -]) diff --git a/src/editfns.c b/src/editfns.c index 98187df..1dbae8f 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -3851,6 +3851,23 @@ usage: (propertize STRING &rest PROPERTIES) */) return string; } =20 +/* Convert the prefix of STR from ASCII decimal digits to a number. + Set *STR_END to the address of the first non-digit. Return the + number, or PTRDIFF_MAX on overflow. Return 0 if there is no number. + This is like strtol for ptrdiff_t and base 10 and C locale, + except without negative numbers or errno. */ + +static ptrdiff_t +str2num (char *str, char **str_end) +{ + ptrdiff_t n =3D 0; + for (; c_isdigit (*str); str++) + if (INT_MULTIPLY_WRAPV (n, 10, &n) || INT_ADD_WRAPV (n, *str - '0', = &n)) + n =3D PTRDIFF_MAX; + *str_end =3D str; + return n; +} + DEFUN ("format", Fformat, Sformat, 1, MANY, 0, doc: /* Format a string out of a format-string and arguments. The first argument is a format control string. @@ -4057,17 +4074,16 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args= , bool message) digits to print after the '.' for floats, or the max. number of chars to print from a string. */ =20 - uintmax_t num; + ptrdiff_t num; char *num_end; if (c_isdigit (*format)) { - num =3D strtoumax (format, &num_end, 10); + num =3D str2num (format, &num_end); if (*num_end =3D=3D '$') { if (num =3D=3D 0) error ("Invalid format field number 0"); - n =3D min (num, PTRDIFF_MAX); - n--; + n =3D num - 1; format =3D num_end + 1; } } @@ -4095,15 +4111,15 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args= , bool message) space_flag &=3D ! plus_flag; zero_flag &=3D ! minus_flag; =20 - num =3D strtoumax (format, &num_end, 10); + num =3D str2num (format, &num_end); if (max_bufsize <=3D num) string_overflow (); ptrdiff_t field_width =3D num; =20 bool precision_given =3D *num_end =3D=3D '.'; - uintmax_t precision =3D (precision_given - ? strtoumax (num_end + 1, &num_end, 10) - : UINTMAX_MAX); + ptrdiff_t precision =3D (precision_given + ? str2num (num_end + 1, &num_end) + : PTRDIFF_MAX); format =3D num_end; =20 if (format =3D=3D end) @@ -4176,7 +4192,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, = bool message) /* handle case (precision[n] >=3D 0) */ =20 ptrdiff_t prec =3D -1; - if (precision_given && precision <=3D TYPE_MAXIMUM (ptrdiff_t)) + if (precision_given) prec =3D precision; =20 /* lisp_string_width ignores a precision of 0, but GNU @@ -4424,8 +4440,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, = bool message) padding and excess precision. Deal with excess precision first. This happens only when the format specifies ridiculously large precision. */ - uintmax_t excess_precision =3D precision - prec; - uintmax_t leading_zeros =3D 0, trailing_zeros =3D 0; + ptrdiff_t excess_precision + =3D precision_given ? precision - prec : 0; + ptrdiff_t leading_zeros =3D 0, trailing_zeros =3D 0; if (excess_precision) { if (float_conversion) @@ -4451,7 +4468,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, = bool message) =20 /* Compute the total bytes needed for this item, including excess precision and padding. */ - uintmax_t numwidth =3D sprintf_bytes + excess_precision; + ptrdiff_t numwidth; + if (INT_ADD_WRAPV (sprintf_bytes, excess_precision, &numwidth)) + numwidth =3D PTRDIFF_MAX; ptrdiff_t padding =3D numwidth < field_width ? field_width - numwidth : 0; if (max_bufsize - sprintf_bytes <=3D excess_precision diff --git a/src/lread.c b/src/lread.c index 368b86e..f849398 100644 --- a/src/lread.c +++ b/src/lread.c @@ -3495,25 +3495,18 @@ substitute_in_interval (INTERVAL interval, Lisp_O= bject arg) } =20 =0C -#define LEAD_INT 1 -#define DOT_CHAR 2 -#define TRAIL_INT 4 -#define E_EXP 16 - - -/* Convert STRING to a number, assuming base BASE. Return a fixnum if C= P has - integer syntax and fits in a fixnum, else return the nearest float if= CP has - either floating point or integer syntax and BASE is 10, else return n= il. If - IGNORE_TRAILING, consider just the longest prefix of CP that has - valid floating point syntax. Signal an overflow if BASE is not 10 an= d the - number has integer syntax but does not fit. */ +/* Convert STRING to a number, assuming base BASE. Return a fixnum if + STRING has integer syntax and fits in a fixnum, else return the + nearest float if STRING has either floating point or integer syntax + and BASE is 10, else return nil. If IGNORE_TRAILING, consider just + the longest prefix of STRING that has valid floating point syntax. + Signal an overflow if BASE is not 10 and the number has integer + syntax but does not fit. */ =20 Lisp_Object string_to_number (char const *string, int base, bool ignore_trailing) { - int state; char const *cp =3D string; - int leading_digit; bool float_syntax =3D 0; double value =3D 0; =20 @@ -3525,15 +3518,23 @@ string_to_number (char const *string, int base, b= ool ignore_trailing) bool signedp =3D negative || *cp =3D=3D '+'; cp +=3D signedp; =20 - state =3D 0; - - leading_digit =3D digit_to_number (*cp, base); + enum { INTOVERFLOW =3D 1, LEAD_INT =3D 2, DOT_CHAR =3D 4, TRAIL_INT =3D= 8, + E_EXP =3D 16 }; + int state =3D 0; + int leading_digit =3D digit_to_number (*cp, base); + uintmax_t n =3D leading_digit; if (leading_digit >=3D 0) { state |=3D LEAD_INT; - do - ++cp; - while (digit_to_number (*cp, base) >=3D 0); + for (int digit; 0 <=3D (digit =3D digit_to_number (*++cp, base)); = ) + { + if (INT_MULTIPLY_OVERFLOW (n, base)) + state |=3D INTOVERFLOW; + n *=3D base; + if (INT_ADD_OVERFLOW (n, digit)) + state |=3D INTOVERFLOW; + n +=3D digit; + } } if (*cp =3D=3D '.') { @@ -3583,32 +3584,22 @@ string_to_number (char const *string, int base, b= ool ignore_trailing) } =20 float_syntax =3D ((state & (DOT_CHAR|TRAIL_INT)) =3D=3D (DOT_CHAR|= TRAIL_INT) - || state =3D=3D (LEAD_INT|E_EXP)); + || (state & ~INTOVERFLOW) =3D=3D (LEAD_INT|E_EXP)); } =20 /* Return nil if the number uses invalid syntax. If IGNORE_TRAILING, = accept any prefix that matches. Otherwise, the entire string must match. = */ if (! (ignore_trailing ? ((state & LEAD_INT) !=3D 0 || float_syntax) - : (!*cp && ((state & ~DOT_CHAR) =3D=3D LEAD_INT || float_syntax)))) + : (!*cp && ((state & ~(INTOVERFLOW | DOT_CHAR)) =3D=3D LEAD_INT + || float_syntax)))) return Qnil; =20 /* If the number uses integer and not float syntax, and is in C-langua= ge range, use its value, preferably as a fixnum. */ if (leading_digit >=3D 0 && ! float_syntax) { - uintmax_t n; - - /* Fast special case for single-digit integers. This also avoids = a - glitch when BASE is 16 and IGNORE_TRAILING, because in that - case some versions of strtoumax accept numbers like "0x1" that Emacs - does not allow. */ - if (digit_to_number (string[signedp + 1], base) < 0) - return make_number (negative ? -leading_digit : leading_digit); - - errno =3D 0; - n =3D strtoumax (string + signedp, NULL, base); - if (errno =3D=3D ERANGE) + if (state & INTOVERFLOW) { /* Unfortunately there's no simple and accurate way to convert non-base-10 numbers that are out of C-language range. */ --=20 2.9.4 --------------843490A2616B420F0FE4CEAA Content-Type: text/x-patch; name="0002-Limit-format-fields-to-more-POSIX-like-spec.patch" Content-Disposition: attachment; filename="0002-Limit-format-fields-to-more-POSIX-like-spec.patch" Content-Transfer-Encoding: quoted-printable >From 4c9bbc524171fa1022e0cbf4516827be98ae08a5 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 1 Jun 2017 16:03:12 -0700 Subject: [PATCH 2/2] Limit format fields to more POSIX-like spec MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit * doc/lispref/strings.texi (Formatting Strings): Don=E2=80=99t allow mixing numbered with unnumbered format specs. * src/editfns.c (styled_format): Don=E2=80=99t bother checking for field = 0, since it doesn=E2=80=99t crash and the behavior is not specified. * test/src/editfns-tests.el (format-with-field): Adjust tests to match current doc. Add more tests for out-of-range fields. --- doc/lispref/strings.texi | 13 +++++-------- src/editfns.c | 8 ++++---- test/src/editfns-tests.el | 28 ++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 4d33e55..e80e778 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -965,16 +965,13 @@ Formatting Strings decimal number immediately after the initial @samp{%}, followed by a literal dollar sign @samp{$}. It causes the format specification to convert the argument with the given number instead of the next -argument. Argument 1 is the argument just after the format. - - You can mix specifications with and without field numbers. A -specification without a field number that follows a specification with -a field number will convert the argument after the one specified by -the field number: +argument. Field numbers start at 1. A format can contain either +numbered or unnumbered format specifications but not both, except that +@samp{%%} can be mixed with numbered specifications. =20 @example -(format "Argument %2$s, then %s, then %1$s" "x" "y" "z") - @result{} "Argument y, then z, then x" +(format "%2$s, %3$s, %%, %1$s" "x" "y" "z") + @result{} "y, z, %, x" @end example =20 @cindex flags in format specifications diff --git a/src/editfns.c b/src/editfns.c index 1dbae8f..29af25a 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -3900,8 +3900,10 @@ where field is [0-9]+ followed by a literal dollar= "$", flags is [+ #-0]+, width is [0-9]+, and precision is a literal period "." followed by [0-9]+. =20 -If field is given, it must be a one-based argument number; the given -argument is substituted instead of the next one. +If a %-sequence is numbered with a field with positive value N, the +Nth argument is substituted instead of the next one. A format can +contain either numbered or unnumbered %-sequences but not both, except +that %% can be mixed with numbered %-sequences. =20 The + flag character inserts a + before any positive number, while a space inserts a space before any positive number; these flags only @@ -4081,8 +4083,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, = bool message) num =3D str2num (format, &num_end); if (*num_end =3D=3D '$') { - if (num =3D=3D 0) - error ("Invalid format field number 0"); n =3D num - 1; format =3D num_end + 1; } diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index c5923aa..54fb743 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el @@ -178,17 +178,33 @@ transpose-test-get-byte-positions (concat (make-string 2048 ?X) "0"))))) =20 (ert-deftest format-with-field () - (should (equal (format "First argument %2$s, then %s, then %1$s" 1 2 3= ) + (should (equal (format "First argument %2$s, then %3$s, then %1$s" 1 2= 3) "First argument 2, then 3, then 1")) - (should (equal (format "a %2$s %d %1$d %2$S %d %d b" 11 "22" 33 44) + (should (equal (format "a %2$s %3$d %1$d %2$S %3$d %4$d b" 11 "22" 33 = 44) "a 22 33 11 \"22\" 33 44 b")) - (should (equal (format "a %08$s %s b" 1 2 3 4 5 6 7 8 9) "a 8 9 b")) + (should (equal (format "a %08$s %0000000000000000009$s b" 1 2 3 4 5 6 = 7 8 9) + "a 8 9 b")) (should (equal (should-error (format "a %999999$s b" 11)) '(error "Not enough arguments for format string"))) + (should (equal (should-error (format "a %2147483647$s b")) + '(error "Not enough arguments for format string"))) + (should (equal (should-error (format "a %9223372036854775807$s b")) + '(error "Not enough arguments for format string"))) + (should (equal (should-error (format "a %9223372036854775808$s b")) + '(error "Not enough arguments for format string"))) + (should (equal (should-error (format "a %18446744073709551615$s b")) + '(error "Not enough arguments for format string"))) + (should (equal (should-error (format "a %18446744073709551616$s b")) + '(error "Not enough arguments for format string"))) + (should (equal (should-error + (format (format "a %%%d$d b" most-positive-fixnum))) + '(error "Not enough arguments for format string"))) + (should (equal (should-error + (format (format "a %%%d$d b" (+ 1.0 most-positive-fixn= um)))) + '(error "Not enough arguments for format string"))) (should (equal (should-error (format "a %$s b" 11)) '(error "Invalid format operation %$"))) - (should (equal (should-error (format "a %0$s b" 11)) - '(error "Invalid format field number 0"))) - (should (equal (format "a %1$% %s b" 11) "a % 11 b"))) + (should (equal (should-error (format "a %-1$s b" 11)) + '(error "Invalid format operation %$")))) =20 ;;; editfns-tests.el ends here --=20 2.9.4 --------------843490A2616B420F0FE4CEAA--