aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJacob S. Gordon2025-12-21 23:39:42 -0800
committerPaul Eggert2025-12-22 00:15:09 -0800
commitcff022f0c3632c2522e568bd5e96841c1a0271ef (patch)
tree69f6254f17aa949d7c4ccd3c6b444f4866873400 /src
parent1f7c804ab17a0a5b57cc3b32d44dc340caf92b0e (diff)
downloademacs-cff022f0c3632c2522e568bd5e96841c1a0271ef.tar.gz
emacs-cff022f0c3632c2522e568bd5e96841c1a0271ef.zip
Add binary format specifications '%b' and '%B'
These produce the binary representation of a number. '%#b' and '%#B' prefix with '0b' and '0B', respectively. (bug#79990) * etc/NEWS: Announce change. * doc/lispref/strings.texi (Formatting Strings): Describe new format specs and add to comment on reconstructing the value with 'read'. * src/editfns.c (format): Update doc string. (styled_format): Add support for '%b' and '%B'. To remain portable, avoid use of 'sprintf' by converting by hand. * test/src/editfns-tests.el (format-binary-zero, format-binary-floats) (format-binary-nonzero-integers): Add tests. (read-large-integer): Add binary test cases. Co-authored-by: Paul Eggert <eggert@cs.ucla.edu>
Diffstat (limited to 'src')
-rw-r--r--src/editfns.c54
1 files changed, 42 insertions, 12 deletions
diff --git a/src/editfns.c b/src/editfns.c
index b5ba3bde7f9..117722e0e09 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -3349,6 +3349,8 @@ the next available argument, or the argument explicitly specified:
3349 3349
3350%s means produce a string argument. Actually, produces any object with `princ'. 3350%s means produce a string argument. Actually, produces any object with `princ'.
3351%d or %i means produce a signed number in decimal. 3351%d or %i means produce a signed number in decimal.
3352%b means produce a number in binary.
3353%B is like %b, but %#B uses upper case.
3352%o means produce a number in octal. 3354%o means produce a number in octal.
3353%x means produce a number in hex. 3355%x means produce a number in hex.
3354%X is like %x, but uses upper case. 3356%X is like %x, but uses upper case.
@@ -3382,8 +3384,9 @@ space inserts a space before any nonnegative number; these flags
3382affect only numeric %-sequences, and the + flag takes precedence. 3384affect only numeric %-sequences, and the + flag takes precedence.
3383The - and 0 flags affect the width specifier, as described below. 3385The - and 0 flags affect the width specifier, as described below.
3384 3386
3385The # flag means to use an alternate display form for %o, %x, %X, %e, 3387The # flag means to use an alternate display form for %b, %B, %o, %x,
3386%f, and %g sequences: for %o, it ensures that the result begins with 3388%X, %e, %f, and %g sequences: for %b and %B, it prefixes nonzero results
3389with \"0b\" or \"0B\"; for %o, it ensures that the result begins with
3387\"0\"; for %x and %X, it prefixes nonzero results with \"0x\" or \"0X\"; 3390\"0\"; for %x and %X, it prefixes nonzero results with \"0x\" or \"0X\";
3388for %e and %f, it causes a decimal point to be included even if the 3391for %e and %f, it causes a decimal point to be included even if the
3389precision is zero; for %g, it causes a decimal point to be 3392precision is zero; for %g, it causes a decimal point to be
@@ -3395,7 +3398,7 @@ produced representation. The padding, if any, normally goes on the
3395left, but it goes on the right if the - flag is present. The padding 3398left, but it goes on the right if the - flag is present. The padding
3396character is normally a space, but it is 0 if the 0 flag is present. 3399character is normally a space, but it is 0 if the 0 flag is present.
3397The 0 flag is ignored if the - flag is present, or the format sequence 3400The 0 flag is ignored if the - flag is present, or the format sequence
3398is something other than %d, %o, %x, %e, %f, and %g. 3401is something other than %d, %b, %o, %x, %e, %f, and %g.
3399 3402
3400For %e and %f sequences, the number after the "." in the precision 3403For %e and %f sequences, the number after the "." in the precision
3401specifier says how many decimal places to show; if zero, the decimal 3404specifier says how many decimal places to show; if zero, the decimal
@@ -3813,8 +3816,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3813 } 3816 }
3814 else if (! (conversion == 'c' || conversion == 'd' 3817 else if (! (conversion == 'c' || conversion == 'd'
3815 || float_conversion || conversion == 'i' 3818 || float_conversion || conversion == 'i'
3816 || conversion == 'o' || conversion == 'x' 3819 || conversion == 'o'
3817 || conversion == 'X')) 3820 || conversion == 'x' || conversion == 'X'
3821 || conversion == 'b' || conversion == 'B'))
3818 { 3822 {
3819 unsigned char *p = (unsigned char *) format - 1; 3823 unsigned char *p = (unsigned char *) format - 1;
3820 if (multibyte_format) 3824 if (multibyte_format)
@@ -3865,7 +3869,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3865 3869
3866 /* Characters to be inserted after spaces and before 3870 /* Characters to be inserted after spaces and before
3867 leading zeros. This can occur with bignums, since 3871 leading zeros. This can occur with bignums, since
3868 bignum_to_string does only leading '-'. */ 3872 bignum_to_string does only leading '-'.
3873 It can also occur with the conversion specs %b and %B,
3874 which sprintf might not support. */
3869 char prefix[sizeof "-0x" - 1]; 3875 char prefix[sizeof "-0x" - 1];
3870 int prefixlen = 0; 3876 int prefixlen = 0;
3871 3877
@@ -3882,6 +3888,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3882 conversions; for example, the min and max macros are 3888 conversions; for example, the min and max macros are
3883 not suitable here. */ 3889 not suitable here. */
3884 ptrdiff_t sprintf_bytes; 3890 ptrdiff_t sprintf_bytes;
3891 bool nonzero_arg;
3885 if (float_conversion) 3892 if (float_conversion)
3886 { 3893 {
3887 /* Format as a long double if the arg is an integer 3894 /* Format as a long double if the arg is an integer
@@ -3949,20 +3956,28 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3949 bignum_arg: 3956 bignum_arg:
3950 { 3957 {
3951 int base = ((conversion == 'd' || conversion == 'i') ? 10 3958 int base = ((conversion == 'd' || conversion == 'i') ? 10
3952 : conversion == 'o' ? 8 : 16); 3959 : conversion == 'o' ? 8
3960 : conversion == 'x' || conversion == 'X' ? 16
3961 : 2);
3953 sprintf_bytes = bignum_bufsize (arg, base); 3962 sprintf_bytes = bignum_bufsize (arg, base);
3954 if (sprintf_bytes <= buf + bufsize - p) 3963 if (sprintf_bytes <= buf + bufsize - p)
3955 { 3964 {
3956 int signedbase = conversion == 'X' ? -base : base; 3965 int signedbase = conversion == 'X' ? -base : base;
3957 sprintf_bytes = bignum_to_c_string (p, sprintf_bytes, 3966 sprintf_bytes = bignum_to_c_string (p, sprintf_bytes,
3958 arg, signedbase); 3967 arg, signedbase);
3968 nonzero_arg = true;
3969 preformatted_fixnum_arg:
3959 bool negative = p[0] == '-'; 3970 bool negative = p[0] == '-';
3960 prec = min (precision, sprintf_bytes - prefixlen); 3971 prec = min (precision, sprintf_bytes - prefixlen);
3961 prefix[prefixlen] = plus_flag ? '+' : ' '; 3972 prefix[prefixlen] = plus_flag ? '+' : ' ';
3962 prefixlen += (plus_flag | space_flag) & !negative; 3973 prefixlen += (plus_flag | space_flag) & !negative;
3963 prefix[prefixlen] = '0'; 3974 prefix[prefixlen] = '0';
3964 prefix[prefixlen + 1] = conversion; 3975 prefix[prefixlen + 1] = conversion;
3965 prefixlen += sharp_flag && base == 16 ? 2 : 0; 3976 prefixlen += ((sharp_flag & nonzero_arg
3977 && ! (conversion == 'd'
3978 || conversion == 'i'
3979 || conversion == 'o'))
3980 ? 2 : 0);
3966 } 3981 }
3967 } 3982 }
3968 else if (conversion == 'd' || conversion == 'i') 3983 else if (conversion == 'd' || conversion == 'i')
@@ -4013,6 +4028,20 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4013 } 4028 }
4014 } 4029 }
4015 p[0] = negative ? '-' : plus_flag ? '+' : ' '; 4030 p[0] = negative ? '-' : plus_flag ? '+' : ' ';
4031
4032 if (conversion == 'b' || conversion == 'B')
4033 {
4034 int bit_width = stdc_bit_width (x);
4035 char *bits = p + negative;
4036 int nbits = bit_width ? bit_width : prec < 0;
4037 for (int i = 0; i < nbits; i++)
4038 bits[i] = ((x >> (nbits - (i + 1))) & 1) + '0';
4039 bits[nbits] = '\0';
4040 sprintf_bytes = negative + nbits;
4041 nonzero_arg = x != 0;
4042 goto preformatted_fixnum_arg;
4043 }
4044
4016 bool signedp = negative | plus_flag | space_flag; 4045 bool signedp = negative | plus_flag | space_flag;
4017 sprintf_bytes = sprintf (p + signedp, convspec, prec, x); 4046 sprintf_bytes = sprintf (p + signedp, convspec, prec, x);
4018 sprintf_bytes += signedp; 4047 sprintf_bytes += signedp;
@@ -4053,12 +4082,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4053 4082
4054 if (convbytes <= buf + bufsize - p) 4083 if (convbytes <= buf + bufsize - p)
4055 { 4084 {
4085 /* Skip any sign and 0[bBxX] prefixes. */
4056 bool signedp = p[0] == '-' || p[0] == '+' || p[0] == ' '; 4086 bool signedp = p[0] == '-' || p[0] == '+' || p[0] == ' ';
4057 int beglen = (signedp 4087 int beglen = (signedp
4058 + ((p[signedp] == '0' 4088 + ((!float_conversion
4059 && (p[signedp + 1] == 'x' 4089 && p[signedp] == '0'
4060 || p[signedp + 1] == 'X')) 4090 && p[signedp + 1] == conversion)
4061 ? 2 : 0)); 4091 ? 2 : 0));
4062 eassert (prefixlen == 0 || beglen == 0 4092 eassert (prefixlen == 0 || beglen == 0
4063 || (beglen == 1 && p[0] == '-' 4093 || (beglen == 1 && p[0] == '-'
4064 && ! (prefix[0] == '-' || prefix[0] == '+' 4094 && ! (prefix[0] == '-' || prefix[0] == '+'