Add support for binary numbers

This commit is contained in:
Jason Schulz 2015-12-29 15:17:16 -08:00
parent dddbf9c5fa
commit 7ad3f077dc
17 changed files with 350 additions and 95 deletions

View File

@ -371,8 +371,10 @@ CTRL-X Subtract [count] from the number or alphabetic
character at or after the cursor. character at or after the cursor.
The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned
octal and hexadecimal numbers and alphabetic characters. This depends on the binary/octal/hexadecimal numbers and alphabetic characters. This
'nrformats' option. depends on the 'nrformats' option.
- When 'nrformats' includes "bin", Vim considers numbers starting with '0b' or
'0B' as binary.
- When 'nrformats' includes "octal", Vim considers numbers starting with a '0' - When 'nrformats' includes "octal", Vim considers numbers starting with a '0'
to be octal, unless the number includes a '8' or '9'. Other numbers are to be octal, unless the number includes a '8' or '9'. Other numbers are
decimal and may have a preceding minus sign. decimal and may have a preceding minus sign.
@ -386,6 +388,10 @@ octal and hexadecimal numbers and alphabetic characters. This depends on the
under or after the cursor. This is useful to make lists with an alphabetic under or after the cursor. This is useful to make lists with an alphabetic
index. index.
For decimals a leading negative sign is considered for incrementing or
decrementing, for binary and octal and hex values, it won't be considered. To
ignore the sign Visually select the number before using CTRL-A or CTRL-X.
For numbers with leading zeros (including all octal and hexadecimal numbers), For numbers with leading zeros (including all octal and hexadecimal numbers),
Vim preserves the number of characters in the number when possible. CTRL-A on Vim preserves the number of characters in the number when possible. CTRL-A on
"0077" results in "0100", CTRL-X on "0x100" results in "0x0ff". "0077" results in "0100", CTRL-X on "0x100" results in "0x0ff".
@ -397,6 +403,10 @@ octal number.
Note that when 'nrformats' includes "octal", decimal numbers with leading Note that when 'nrformats' includes "octal", decimal numbers with leading
zeros cause mistakes, because they can be confused with octal numbers. zeros cause mistakes, because they can be confused with octal numbers.
Note similarly, when 'nrformats' includes "bin", binary numbers with a leading
'0x' or '0X' can be interpreted as hexadecimal rather than binary since '0b'
are valid hexadecimal digits.
The CTRL-A command is very useful in a macro. Example: Use the following The CTRL-A command is very useful in a macro. Example: Use the following
steps to make a numbered list. steps to make a numbered list.
@ -1602,7 +1612,7 @@ Vim has a sorting function and a sorting command. The sorting function can be
found here: |sort()|, |uniq()|. found here: |sort()|, |uniq()|.
*:sor* *:sort* *:sor* *:sort*
:[range]sor[t][!] [i][u][r][n][x][o] [/{pattern}/] :[range]sor[t][!] [i][u][r][n][x][o][b] [/{pattern}/]
Sort lines in [range]. When no range is given all Sort lines in [range]. When no range is given all
lines are sorted. lines are sorted.
@ -1622,6 +1632,9 @@ found here: |sort()|, |uniq()|.
With [o] sorting is done on the first octal number in With [o] sorting is done on the first octal number in
the line (after or inside a {pattern} match). the line (after or inside a {pattern} match).
With [b] sorting is done on the first binary number in
the line (after or inside a {pattern} match).
With [u] only keep the first of a sequence of With [u] only keep the first of a sequence of
identical lines (ignoring case when [i] is used). identical lines (ignoring case when [i] is used).
Without this flag, a sequence of identical lines Without this flag, a sequence of identical lines

View File

@ -65,14 +65,16 @@ the Number. Examples:
Number 0 --> String "0" ~ Number 0 --> String "0" ~
Number -1 --> String "-1" ~ Number -1 --> String "-1" ~
*octal* *octal*
Conversion from a String to a Number is done by converting the first digits Conversion from a String to a Number is done by converting the first digits to
to a number. Hexadecimal "0xf9" and Octal "017" numbers are recognized. If a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are
the String doesn't start with digits, the result is zero. Examples: recognized. If the String doesn't start with digits, the result is zero.
Examples:
String "456" --> Number 456 ~ String "456" --> Number 456 ~
String "6bar" --> Number 6 ~ String "6bar" --> Number 6 ~
String "foo" --> Number 0 ~ String "foo" --> Number 0 ~
String "0xf1" --> Number 241 ~ String "0xf1" --> Number 241 ~
String "0100" --> Number 64 ~ String "0100" --> Number 64 ~
String "0b101" --> Number 5 ~
String "-8" --> Number -8 ~ String "-8" --> Number -8 ~
String "+8" --> Number 0 ~ String "+8" --> Number 0 ~
@ -4912,6 +4914,9 @@ printf({fmt}, {expr1} ...) *printf()*
%c single byte %c single byte
%d decimal number %d decimal number
%5d decimal number padded with spaces to 5 characters %5d decimal number padded with spaces to 5 characters
%b binary number
%08b binary number padded with zeros to at least 8 characters
%B binary number using upper case letters
%x hex number %x hex number
%04x hex number padded with zeros to at least 4 characters %04x hex number padded with zeros to at least 4 characters
%X hex number using upper case letters %X hex number using upper case letters
@ -4998,20 +5003,19 @@ printf({fmt}, {expr1} ...) *printf()*
The conversion specifiers and their meanings are: The conversion specifiers and their meanings are:
*printf-d* *printf-o* *printf-x* *printf-X* *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
doxX The Number argument is converted to signed decimal dbBoxX The Number argument is converted to signed decimal (d),
(d), unsigned octal (o), or unsigned hexadecimal (x unsigned binary (b and B), unsigned octal (o), or
and X) notation. The letters "abcdef" are used for unsigned hexadecimal (x and X) notation. The letters
x conversions; the letters "ABCDEF" are used for X "abcdef" are used for x conversions; the letters
conversions. "ABCDEF" are used for X conversions. The precision, if
The precision, if any, gives the minimum number of any, gives the minimum number of digits that must
digits that must appear; if the converted value appear; if the converted value requires fewer digits, it
requires fewer digits, it is padded on the left with is padded on the left with zeros. In no case does a
zeros. non-existent or small field width cause truncation of a
In no case does a non-existent or small field width numeric field; if the result of a conversion is wider
cause truncation of a numeric field; if the result of than the field width, the field is expanded to contain
a conversion is wider than the field width, the field the conversion result.
is expanded to contain the conversion result.
*printf-c* *printf-c*
c The Number argument is converted to a byte, and the c The Number argument is converted to a byte, and the
@ -6127,12 +6131,14 @@ str2float( {expr}) *str2float()*
str2nr( {expr} [, {base}]) *str2nr()* str2nr( {expr} [, {base}]) *str2nr()*
Convert string {expr} to a number. Convert string {expr} to a number.
{base} is the conversion base, it can be 8, 10 or 16. {base} is the conversion base, it can be 2, 8, 10 or 16.
When {base} is omitted base 10 is used. This also means that When {base} is omitted base 10 is used. This also means that
a leading zero doesn't cause octal conversion to be used, as a leading zero doesn't cause octal conversion to be used, as
with the default String to Number conversion. with the default String to Number conversion.
When {base} is 16 a leading "0x" or "0X" is ignored. With a When {base} is 16 a leading "0x" or "0X" is ignored. With a
different base the result will be zero. different base the result will be zero. Similarly, when {base}
is 8 a leading "0" is ignored, and when {base} is 2 a leading
"0b" or "0B" is ignored.
Text after the number is silently ignored. Text after the number is silently ignored.

View File

@ -122,6 +122,14 @@ static inline bool ascii_isxdigit(int c)
|| (c >= 'A' && c <= 'F'); || (c >= 'A' && c <= 'F');
} }
/// Checks if `c` is a binary digit, that is, 0-1.
///
/// @see {ascii_isdigit}
static inline bool ascii_isbdigit(int c)
{
return (c == '0' || c == '1');
}
/// Checks if `c` is a white-space character, that is, /// Checks if `c` is a white-space character, that is,
/// one of \f, \n, \r, \t, \v. /// one of \f, \n, \r, \t, \v.
/// ///

View File

@ -1454,6 +1454,20 @@ char_u* skipdigits(char_u *q)
return p; return p;
} }
/// skip over binary digits
///
/// @param q
///
/// @return Pointer to the character after the skipped digits.
char_u* skipbin(char_u *q)
{
char_u *p = q;
while (ascii_isbdigit(*p)) /* skip to next non-digit */
++p;
return p;
}
/// skip over digits and hex characters /// skip over digits and hex characters
/// ///
/// @param q /// @param q
@ -1485,6 +1499,20 @@ char_u* skiptodigit(char_u *q)
return p; return p;
} }
/// skip to binary character (or NUL after the string)
///
/// @param q
///
/// @return Pointer to the binary character or (NUL after the string).
char_u* skiptobin(char_u *q)
{
char_u *p = q;
while (*p != NUL && !ascii_isbdigit(*p)) /* skip to next digit */
++p;
return p;
}
/// skip to hex character (or NUL after the string) /// skip to hex character (or NUL after the string)
/// ///
/// @param q /// @param q
@ -1720,33 +1748,38 @@ int vim_isblankline(char_u *lbuf)
} }
/// Convert a string into a long and/or unsigned long, taking care of /// Convert a string into a long and/or unsigned long, taking care of
/// hexadecimal and octal numbers. Accepts a '-' sign. /// hexadecimal, octal and binary numbers. Accepts a '-' sign.
/// If "hexp" is not NULL, returns a flag to indicate the type of the number: /// If "prep" is not NULL, returns a flag to indicate the type of the number:
/// 0 decimal /// 0 decimal
/// '0' octal /// '0' octal
/// 'B' bin
/// 'b' bin
/// 'X' hex /// 'X' hex
/// 'x' hex /// 'x' hex
/// If "len" is not NULL, the length of the number in characters is returned. /// If "len" is not NULL, the length of the number in characters is returned.
/// If "nptr" is not NULL, the signed result is returned in it. /// If "nptr" is not NULL, the signed result is returned in it.
/// If "unptr" is not NULL, the unsigned result is returned in it. /// If "unptr" is not NULL, the unsigned result is returned in it.
/// If "dobin" is non-zero recognize binary numbers, when > 1 always assume
/// binary number.
/// If "dooct" is non-zero recognize octal numbers, when > 1 always assume /// If "dooct" is non-zero recognize octal numbers, when > 1 always assume
/// octal number. /// octal number.
/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume /// If "dohex" is non-zero recognize hex numbers, when > 1 always assume
/// hex number. /// hex number.
/// ///
/// @param start /// @param start
/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex, /// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
// '0' = octal // '0' = octal, 'b' or 'B' is bin
/// @param len Returns the detected length of number. /// @param len Returns the detected length of number.
/// @param dobin recognize binary number
/// @param dooct recognize octal number /// @param dooct recognize octal number
/// @param dohex recognize hex number /// @param dohex recognize hex number
/// @param nptr Returns the signed result. /// @param nptr Returns the signed result.
/// @param unptr Returns the unsigned result. /// @param unptr Returns the unsigned result.
void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, void vim_str2nr(char_u *start, int *prep, int *len, int dobin, int dooct, int dohex,
long *nptr, unsigned long *unptr) long *nptr, unsigned long *unptr)
{ {
char_u *ptr = start; char_u *ptr = start;
int hex = 0; // default is decimal int pre = 0; // default is decimal
int negative = FALSE; int negative = FALSE;
unsigned long un = 0; unsigned long un = 0;
int n; int n;
@ -1756,31 +1789,35 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
++ptr; ++ptr;
} }
// Recognize hex and octal. // Recognize hex, octal, and bin.
if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) {
hex = ptr[1]; pre = ptr[1];
if (dohex if (dohex
&& ((hex == 'X') || (hex == 'x')) && ((pre == 'X') || (pre == 'x'))
&& ascii_isxdigit(ptr[2])) { && ascii_isxdigit(ptr[2])) {
// hexadecimal // hexadecimal
ptr += 2; ptr += 2;
} else if (dobin
&& ((pre == 'B') || (pre == 'b'))
&& ascii_isbdigit(ptr[2])) {
// binary
ptr += 2;
} else { } else {
// default is decimal // default is decimal
hex = 0; pre = 0;
if (dooct) { if (dooct) {
// Don't interpret "0", "08" or "0129" as octal. // Don't interpret "0", "08" or "0129" as octal.
for (n = 1; ascii_isdigit(ptr[n]); ++n) { for (n = 1; ascii_isdigit(ptr[n]); ++n) {
if (ptr[n] > '7') { if (ptr[n] > '7') {
// can't be octal // can't be octal
hex = 0; pre = 0;
break; break;
} }
if (ptr[n] >= '0') { if (ptr[n] >= '0') {
// assume octal // assume octal
hex = '0'; pre = '0';
} }
} }
} }
@ -1788,28 +1825,38 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
} }
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks. // Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
if ((hex == '0') || (dooct > 1)) { if ((pre == 'B') || (pre == 'b') || (dobin > 1)) {
// bin
if (pre != 0)
n += 2; // skip over "0b"
while ('0' <= *ptr && *ptr <= '1') {
un = 2 * un + (unsigned long)(*ptr - '0');
++ptr;
}
} else if ((pre == '0') || (dooct > 1)) {
// octal // octal
while ('0' <= *ptr && *ptr <= '7') { while ('0' <= *ptr && *ptr <= '7') {
un = 8 * un + (unsigned long)(*ptr - '0'); un = 8 * un + (unsigned long)(*ptr - '0');
ptr++; ptr++;
} }
} else if ((hex != 0) || (dohex > 1)) { } else if (pre != 0 || dohex > 1) {
// hex // hex
if (pre != 0)
n += 2; // skip over "0x"
while (ascii_isxdigit(*ptr)) { while (ascii_isxdigit(*ptr)) {
un = 16 * un + (unsigned long)hex2nr(*ptr); un = 16 * un + (unsigned long)hex2nr(*ptr);
ptr++; ++ptr;
} }
} else { } else {
// decimal // decimal
while (ascii_isdigit(*ptr)) { while (ascii_isdigit(*ptr)) {
un = 10 * un + (unsigned long)(*ptr - '0'); un = 10 * un + (unsigned long)(*ptr - '0');
ptr++; ++ptr;
} }
} }
if (hexp != NULL) { if (prep != NULL) {
*hexp = hex; *prep = pre;
} }
if (len != NULL) { if (len != NULL) {

View File

@ -1149,7 +1149,7 @@ call_vim_function (
len = 0; len = 0;
else else
/* Recognize a number argument, the others must be strings. */ /* Recognize a number argument, the others must be strings. */
vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL); vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, TRUE, &n, NULL);
if (len != 0 && len == (int)STRLEN(argv[i])) { if (len != 0 && len == (int)STRLEN(argv[i])) {
argvars[i].v_type = VAR_NUMBER; argvars[i].v_type = VAR_NUMBER;
argvars[i].vval.v_number = n; argvars[i].vval.v_number = n;
@ -4127,7 +4127,7 @@ eval7 (
rettv->vval.v_float = f; rettv->vval.v_float = f;
} }
} else { } else {
vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL); vim_str2nr(*arg, NULL, &len, TRUE, TRUE, TRUE, &n, NULL);
*arg += len; *arg += len;
if (evaluate) { if (evaluate) {
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
@ -15982,7 +15982,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[1].v_type != VAR_UNKNOWN) {
base = get_tv_number(&argvars[1]); base = get_tv_number(&argvars[1]);
if (base != 8 && base != 10 && base != 16) { if (base != 2 && base != 8 && base != 10 && base != 16) {
EMSG(_(e_invarg)); EMSG(_(e_invarg));
return; return;
} }
@ -15991,7 +15991,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv)
p = skipwhite(get_tv_string(&argvars[0])); p = skipwhite(get_tv_string(&argvars[0]));
if (*p == '+') if (*p == '+')
p = skipwhite(p + 1); p = skipwhite(p + 1);
vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL); vim_str2nr(p, NULL, NULL, base == 2 ? 2 : 0, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL);
rettv->vval.v_number = n; rettv->vval.v_number = n;
} }
@ -18273,7 +18273,7 @@ long get_tv_number_chk(typval_T *varp, int *denote)
case VAR_STRING: case VAR_STRING:
if (varp->vval.v_string != NULL) if (varp->vval.v_string != NULL)
vim_str2nr(varp->vval.v_string, NULL, NULL, vim_str2nr(varp->vval.v_string, NULL, NULL,
TRUE, TRUE, &n, NULL); TRUE, TRUE, TRUE, &n, NULL);
return n; return n;
case VAR_LIST: case VAR_LIST:
EMSG(_("E745: Using a List as a Number")); EMSG(_("E745: Using a List as a Number"));

View File

@ -348,6 +348,7 @@ void ex_sort(exarg_T *eap)
long deleted; long deleted;
colnr_T start_col; colnr_T start_col;
colnr_T end_col; colnr_T end_col;
int sort_bin; /* sort on bin number */
int sort_oct; /* sort on octal number */ int sort_oct; /* sort on octal number */
int sort_hex; /* sort on hex number */ int sort_hex; /* sort on hex number */
@ -362,7 +363,7 @@ void ex_sort(exarg_T *eap)
regmatch.regprog = NULL; regmatch.regprog = NULL;
sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); sorti_T *nrs = xmalloc(count * sizeof(sorti_T));
sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0; sort_abort = sort_ic = sort_rx = sort_nr = sort_bin = sort_oct = sort_hex = 0;
for (p = eap->arg; *p != NUL; ++p) { for (p = eap->arg; *p != NUL; ++p) {
if (ascii_iswhite(*p)) if (ascii_iswhite(*p))
@ -373,6 +374,8 @@ void ex_sort(exarg_T *eap)
sort_rx = TRUE; sort_rx = TRUE;
else if (*p == 'n') else if (*p == 'n')
sort_nr = 2; sort_nr = 2;
else if (*p == 'b')
sort_bin = 2;
else if (*p == 'o') else if (*p == 'o')
sort_oct = 2; sort_oct = 2;
else if (*p == 'x') else if (*p == 'x')
@ -410,14 +413,14 @@ void ex_sort(exarg_T *eap)
} }
} }
/* Can only have one of 'n', 'o' and 'x'. */ /* Can only have one of 'n', 'b', 'o' and 'x'. */
if (sort_nr + sort_oct + sort_hex > 2) { if (sort_nr + sort_bin + sort_oct + sort_hex > 2) {
EMSG(_(e_invarg)); EMSG(_(e_invarg));
goto sortend; goto sortend;
} }
/* From here on "sort_nr" is used as a flag for any number sorting. */ /* From here on "sort_nr" is used as a flag for any number sorting. */
sort_nr += sort_oct + sort_hex; sort_nr += sort_bin + sort_oct + sort_hex;
/* /*
* Make an array with all line numbers. This avoids having to copy all * Make an array with all line numbers. This avoids having to copy all
@ -454,6 +457,8 @@ void ex_sort(exarg_T *eap)
p = s + start_col; p = s + start_col;
if (sort_hex) if (sort_hex)
s = skiptohex(p); s = skiptohex(p);
else if (sort_bin)
s = skiptobin(p);
else else
s = skiptodigit(p); s = skiptodigit(p);
if (s > p && s[-1] == '-') if (s > p && s[-1] == '-')
@ -462,7 +467,7 @@ void ex_sort(exarg_T *eap)
/* empty line should sort before any number */ /* empty line should sort before any number */
nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; nrs[lnum - eap->line1].start_col_nr = -MAXLNUM;
else else
vim_str2nr(s, NULL, NULL, sort_oct, sort_hex, vim_str2nr(s, NULL, NULL, sort_bin, sort_oct, sort_hex,
&nrs[lnum - eap->line1].start_col_nr, NULL); &nrs[lnum - eap->line1].start_col_nr, NULL);
*s2 = c; *s2 = c;
} else { } else {

View File

@ -4780,7 +4780,7 @@ int get_list_range(char_u **str, int *num1, int *num2)
*str = skipwhite(*str); *str = skipwhite(*str);
if (**str == '-' || ascii_isdigit(**str)) { /* parse "from" part of range */ if (**str == '-' || ascii_isdigit(**str)) { /* parse "from" part of range */
vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); vim_str2nr(*str, NULL, &len, FALSE, FALSE, FALSE, &num, NULL);
*str += len; *str += len;
*num1 = (int)num; *num1 = (int)num;
first = TRUE; first = TRUE;
@ -4788,7 +4788,7 @@ int get_list_range(char_u **str, int *num1, int *num2)
*str = skipwhite(*str); *str = skipwhite(*str);
if (**str == ',') { /* parse "to" part of range */ if (**str == ',') { /* parse "to" part of range */
*str = skipwhite(*str + 1); *str = skipwhite(*str + 1);
vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); vim_str2nr(*str, NULL, &len, FALSE, FALSE, FALSE, &num, NULL);
if (len > 0) { if (len > 0) {
*num2 = (int)num; *num2 = (int)num;
*str = skipwhite(*str + len); *str = skipwhite(*str + len);

View File

@ -575,7 +575,7 @@ find_special_key (
if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3])
bp += 3; /* skip t_xx, xx may be '-' or '>' */ bp += 3; /* skip t_xx, xx may be '-' or '>' */
else if (STRNICMP(bp, "char-", 5) == 0) { else if (STRNICMP(bp, "char-", 5) == 0) {
vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, NULL, NULL); vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, TRUE, NULL, NULL);
bp += l + 5; bp += l + 5;
break; break;
} }
@ -602,7 +602,7 @@ find_special_key (
if (STRNICMP(last_dash + 1, "char-", 5) == 0 if (STRNICMP(last_dash + 1, "char-", 5) == 0
&& ascii_isdigit(last_dash[6])) { && ascii_isdigit(last_dash[6])) {
/* <Char-123> or <Char-033> or <Char-0x33> */ /* <Char-123> or <Char-033> or <Char-0x33> */
vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, NULL, &n); vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, TRUE, NULL, &n);
key = (int)n; key = (int)n;
} else { } else {
/* /*

View File

@ -3037,7 +3037,7 @@ static double tv_float(typval_T *tvs, int *idxp)
* http://www.ijs.si/software/snprintf/ * http://www.ijs.si/software/snprintf/
* *
* This snprintf() only supports the following conversion specifiers: * This snprintf() only supports the following conversion specifiers:
* s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) * s, c, b, B, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
* with flags: '-', '+', ' ', '0' and '#'. * with flags: '-', '+', ' ', '0' and '#'.
* An asterisk is supported for field width as well as precision. * An asterisk is supported for field width as well as precision.
* *
@ -3295,8 +3295,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
break; break;
case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { case 'd': case 'u': case 'b': case 'B': case 'o': case 'x': case 'X': case 'p': {
// u, o, x, X and p conversion specifiers imply the value is unsigned; // u, b, B, o, x, X and p conversion specifiers imply the value is unsigned;
// d implies a signed value // d implies a signed value
// 0 if numeric argument is zero (or if pointer is NULL for 'p'), // 0 if numeric argument is zero (or if pointer is NULL for 'p'),
@ -3399,7 +3399,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// leave negative numbers for sprintf to handle, to // leave negative numbers for sprintf to handle, to
// avoid handling tricky cases like (short int)-32768 // avoid handling tricky cases like (short int)-32768
} else if (alternate_form) { } else if (alternate_form) {
if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) { if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' ||
fmt_spec == 'b' || fmt_spec == 'B')) {
tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = '0';
tmp[str_arg_l++] = fmt_spec; tmp[str_arg_l++] = fmt_spec;
} }
@ -3411,7 +3412,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
precision = 1; // default precision is 1 precision = 1; // default precision is 1
if (precision == 0 && arg_sign == 0) { if (precision == 0 && arg_sign == 0) {
// when zero value is formatted with an explicit precision 0, // when zero value is formatted with an explicit precision 0,
// resulting formatted string is empty (d, i, u, o, x, X, p) // resulting formatted string is empty (d, i, u, b, B, o, x, X, p)
} else { } else {
char f[5]; char f[5];
int f_l = 0; int f_l = 0;
@ -3441,6 +3442,36 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg); case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg);
break; break;
} }
} else if (fmt_spec == 'b' || fmt_spec == 'B') {
//binary
size_t bits = 0;
switch (length_modifier) {
case '\0':
case 'h': for (bits = sizeof(unsigned) * 8; bits > 0; bits--)
if ((uint_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((uint_arg >> --bits) & 0x1) ? '1' : '0';
break;
case 'l': for (bits = sizeof(unsigned long) * 8; bits > 0; bits--)
if ((ulong_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((ulong_arg >> --bits) & 0x1) ? '1' : '0';
break;
case '2': for (bits = sizeof(unsigned long long) * 8; bits > 0; bits--)
if ((ulong_long_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((ulong_long_arg >> --bits) & 0x1) ? '1' : '0';
break;
case 'z': for (bits = sizeof(size_t) * 8; bits > 0; bits--)
if ((size_t_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((size_t_arg >> --bits) & 0x1) ? '1' : '0';
break;
}
} else { } else {
// unsigned // unsigned
switch (length_modifier) { switch (length_modifier) {
@ -3464,7 +3495,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (zero_padding_insertion_ind + 1 < str_arg_l if (zero_padding_insertion_ind + 1 < str_arg_l
&& tmp[zero_padding_insertion_ind] == '0' && tmp[zero_padding_insertion_ind] == '0'
&& (tmp[zero_padding_insertion_ind + 1] == 'x' && (tmp[zero_padding_insertion_ind + 1] == 'x'
|| tmp[zero_padding_insertion_ind + 1] == 'X')) || tmp[zero_padding_insertion_ind + 1] == 'X'
|| tmp[zero_padding_insertion_ind + 1] == 'b'
|| tmp[zero_padding_insertion_ind + 1] == 'B'))
zero_padding_insertion_ind += 2; zero_padding_insertion_ind += 2;
} }

View File

@ -4197,7 +4197,7 @@ int do_addsub(int command, linenr_T Prenum1)
int col; int col;
char_u *buf1; char_u *buf1;
char_u buf2[NUMBUFLEN]; char_u buf2[NUMBUFLEN];
int hex; /* 'X' or 'x': hex; '0': octal */ int pre; /* 'X' or 'x': hex; '0': octal; 'B' or 'b': bin */
static int hexupper = FALSE; /* 0xABC */ static int hexupper = FALSE; /* 0xABC */
unsigned long n, oldn; unsigned long n, oldn;
char_u *ptr; char_u *ptr;
@ -4206,6 +4206,7 @@ int do_addsub(int command, linenr_T Prenum1)
int todel; int todel;
int dohex; int dohex;
int dooct; int dooct;
int dobin;
int doalp; int doalp;
int firstdigit; int firstdigit;
int negative; int negative;
@ -4213,6 +4214,7 @@ int do_addsub(int command, linenr_T Prenum1)
dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */ dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */
dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */ dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */
dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); /* "Bin" */
doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */ doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */
ptr = get_cursor_line_ptr(); ptr = get_cursor_line_ptr();
@ -4222,19 +4224,44 @@ int do_addsub(int command, linenr_T Prenum1)
* First check if we are on a hexadecimal number, after the "0x". * First check if we are on a hexadecimal number, after the "0x".
*/ */
col = curwin->w_cursor.col; col = curwin->w_cursor.col;
if (dobin)
while (col > 0 && ascii_isbdigit(ptr[col]))
--col;
if (dohex) if (dohex)
while (col > 0 && ascii_isxdigit(ptr[col])) while (col > 0 && ascii_isxdigit(ptr[col]))
--col; --col;
if ( dohex if ( dobin
&& col > 0 && dohex
&& ! ((col > 0
&& (ptr[col] == 'X' && (ptr[col] == 'X'
|| ptr[col] == 'x') || ptr[col] == 'x')
&& ptr[col - 1] == '0' && ptr[col - 1] == '0'
&& ascii_isxdigit(ptr[col + 1])) { && ascii_isxdigit(ptr[col + 1])))) {
/*
* Found hexadecimal number, move to its start. /* In case of binary/hexadecimal pattern overlap match, rescan */
*/
--col; col = curwin->w_cursor.col;
while (col > 0 && ascii_isdigit(ptr[col]))
col--;
}
if (( dohex
&& col > 0
&& (ptr[col] == 'X'
|| ptr[col] == 'x')
&& ptr[col - 1] == '0'
&& ascii_isxdigit(ptr[col + 1])) ||
( dobin
&& col > 0
&& (ptr[col] == 'B'
|| ptr[col] == 'b')
&& ptr[col - 1] == '0'
&& ascii_isbdigit(ptr[col + 1]))) {
/* Found hexadecimal or binary number, move to its start. */
--col;
} else { } else {
/* /*
* Search forward and then backward to find the start of number. * Search forward and then backward to find the start of number.
@ -4297,10 +4324,10 @@ int do_addsub(int command, linenr_T Prenum1)
} }
/* get the number value (unsigned) */ /* get the number value (unsigned) */
vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n); vim_str2nr(ptr + col, &pre, &length, dobin, dooct, dohex, NULL, &n);
/* ignore leading '-' for hex and octal numbers */ /* ignore leading '-' for hex, octal and bin numbers */
if (hex && negative) { if (pre && negative) {
++col; ++col;
--length; --length;
negative = FALSE; negative = FALSE;
@ -4320,7 +4347,7 @@ int do_addsub(int command, linenr_T Prenum1)
n += (unsigned long)Prenum1; n += (unsigned long)Prenum1;
/* handle wraparound for decimal numbers */ /* handle wraparound for decimal numbers */
if (!hex) { if (!pre) {
if (subtract) { if (subtract) {
if (n > oldn) { if (n > oldn) {
n = 1 + (n ^ (unsigned long)-1); n = 1 + (n ^ (unsigned long)-1);
@ -4370,23 +4397,38 @@ int do_addsub(int command, linenr_T Prenum1)
if (negative) { if (negative) {
*ptr++ = '-'; *ptr++ = '-';
} }
if (hex) { if (pre) {
*ptr++ = '0'; *ptr++ = '0';
--length; --length;
} }
if (hex == 'x' || hex == 'X') { if (pre == 'b' || pre == 'B'
*ptr++ = hex; || pre == 'x' || pre == 'X') {
*ptr++ = pre;
--length; --length;
} }
/* /*
* Put the number characters in buf2[]. * Put the number characters in buf2[].
*/ */
if (hex == 0) if (pre == 'b' || pre == 'B') {
size_t bits = 0;
size_t pos = 0;
/* leading zeros */
for (bits = 8 * sizeof(unsigned long); bits > 0; bits--)
if ((n >> (bits - 1)) & 0x1) break;
while (bits > 0)
buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0';
buf2[pos] = '\0';
} else if (pre == 0)
sprintf((char *)buf2, "%" PRIu64, (uint64_t)n); sprintf((char *)buf2, "%" PRIu64, (uint64_t)n);
else if (hex == '0') else if (pre == '0')
sprintf((char *)buf2, "%" PRIo64, (uint64_t)n); sprintf((char *)buf2, "%" PRIo64, (uint64_t)n);
else if (hex && hexupper) else if (pre && hexupper)
sprintf((char *)buf2, "%" PRIX64, (uint64_t)n); sprintf((char *)buf2, "%" PRIX64, (uint64_t)n);
else else
sprintf((char *)buf2, "%" PRIx64, (uint64_t)n); sprintf((char *)buf2, "%" PRIx64, (uint64_t)n);
@ -4398,7 +4440,7 @@ int do_addsub(int command, linenr_T Prenum1)
* Don't do this when * Don't do this when
* the result may look like an octal number. * the result may look like an octal number.
*/ */
if (firstdigit == '0' && !(dooct && hex == 0)) if (firstdigit == '0' && !(dooct && pre == 0))
while (length-- > 0) while (length-- > 0)
*ptr++ = '0'; *ptr++ = '0';
*ptr = NUL; *ptr = NUL;

View File

@ -260,7 +260,7 @@ typedef struct vimoption {
static char *(p_ambw_values[]) = {"single", "double", NULL}; static char *(p_ambw_values[]) = {"single", "double", NULL};
static char *(p_bg_values[]) = {"light", "dark", NULL}; static char *(p_bg_values[]) = {"light", "dark", NULL};
static char *(p_nf_values[]) = {"octal", "hex", "alpha", NULL}; static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", NULL};
static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL}; static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
static char *(p_wop_values[]) = {"tagfile", NULL}; static char *(p_wop_values[]) = {"tagfile", NULL};
static char *(p_wak_values[]) = {"yes", "menu", "no", NULL}; static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
@ -1431,7 +1431,7 @@ do_set (
} else if (*arg == '-' || ascii_isdigit(*arg)) { } else if (*arg == '-' || ascii_isdigit(*arg)) {
// Allow negative (for 'undolevels'), octal and // Allow negative (for 'undolevels'), octal and
// hex numbers. // hex numbers.
vim_str2nr(arg, NULL, &i, true, true, &value, NULL); vim_str2nr(arg, NULL, &i, true, true, true, &value, NULL);
if (arg[i] != NUL && !ascii_iswhite(arg[i])) { if (arg[i] != NUL && !ascii_iswhite(arg[i])) {
errmsg = e_invarg; errmsg = e_invarg;
goto skip; goto skip;

View File

@ -1599,7 +1599,7 @@ return {
deny_duplicates=true, deny_duplicates=true,
alloced=true, alloced=true,
varname='p_nf', varname='p_nf',
defaults={if_true={vi="octal,hex", vim="hex"}} defaults={if_true={vi="bin,octal,hex", vim="bin,hex"}}
}, },
{ {
full_name='number', abbreviation='nu', full_name='number', abbreviation='nu',

View File

@ -1095,7 +1095,9 @@ spell_check (
// 0X99FF. But always do check spelling to find "3GPP" and "11 // 0X99FF. But always do check spelling to find "3GPP" and "11
// julifeest". // julifeest".
if (*ptr >= '0' && *ptr <= '9') { if (*ptr >= '0' && *ptr <= '9') {
if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
mi.mi_end = skipbin(ptr + 2);
else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
mi.mi_end = skiphex(ptr + 2); mi.mi_end = skiphex(ptr + 2);
else else
mi.mi_end = skipdigits(ptr); mi.mi_end = skipdigits(ptr);

View File

@ -35,7 +35,7 @@ Error: configure did not run properly.Check auto/config.log.
#include "nvim/os/os_defs.h" /* bring lots of system header files */ #include "nvim/os/os_defs.h" /* bring lots of system header files */
#define NUMBUFLEN 30 /* length of a buffer to store a number in ASCII */ #define NUMBUFLEN 65 /* length of a buffer to store a number in ASCII */
#define MAX_TYPENR 65535 #define MAX_TYPENR 65535

View File

@ -0,0 +1,58 @@
local helpers = require('test.functional.helpers')
local clear = helpers.clear
local eq = helpers.eq
local funcs = helpers.funcs
local exc_exec = helpers.exc_exec
describe('printf()', function()
it('works with zero and %b', function()
eq('0', funcs.printf('%lb', 0))
eq('0', funcs.printf('%llb', 0))
eq('0', funcs.printf('%zb', 0))
end)
it('works with one and %b', function()
eq('1', funcs.printf('%b', 1))
eq('1', funcs.printf('%lb', 1))
eq('1', funcs.printf('%llb', 1))
eq('1', funcs.printf('%zb', 1))
end)
it('works with 0xff and %b', function()
eq('11111111', funcs.printf('%b', 0xff))
eq('11111111', funcs.printf('%lb', 0xff))
eq('11111111', funcs.printf('%llb', 0xff))
eq('11111111', funcs.printf('%zb', 0xff))
end)
it('accepts width modifier with %b', function()
eq(' 1', funcs.printf('%3b', 1))
end)
it('accepts prefix modifier with %b', function()
eq('0b1', funcs.printf('%#b', 1))
end)
it('writes capital B with %B', function()
eq('0B1', funcs.printf('%#B', 1))
end)
it('accepts prefix, zero-fill and width modifiers with %b', function()
eq('0b001', funcs.printf('%#05b', 1))
end)
it('accepts prefix and width modifiers with %b', function()
eq(' 0b1', funcs.printf('%#5b', 1))
end)
it('does not write prefix for zero with prefix and width modifier used with %b', function()
eq(' 0', funcs.printf('%#5b', 0))
end)
it('accepts precision modifier with %b', function()
eq('00000', funcs.printf('%.5b', 0))
end)
it('accepts all modifiers with %b at once', function()
-- zero-fill modifier is ignored when used with left-align
-- force-sign and add-blank are ignored
-- use-grouping-characters modifier is ignored always
eq('0b00011 ', funcs.printf('% \'+#0-10.5b', 3))
end)
it('errors out when %b modifier is used for a list', function()
eq('Vim(call):E745: Using a List as a Number', exc_exec('call printf("%b", [])'))
end)
it('errors out when %b modifier is used for a float', function()
eq('Vim(call):E805: Using a Float as a Number', exc_exec('call printf("%b", 3.1415926535)'))
end)
end)

View File

@ -11,34 +11,40 @@ describe('increment and decrement commands', function()
it('should work', function() it('should work', function()
-- Insert some numbers in various bases. -- Insert some numbers in various bases.
insert([[ insert([[
100 0x100 077 0 0b101 100 0x100 077 0
100 0x100 077 0b101 100 0x100 077
100 0x100 077 0xfF 0xFf 100 0x100 077 0xfF 0xFf
100 0x100 077]]) 100 0x100 077
0x0b101 0b1101]])
-- Increment and decrement numbers in the first row, interpreting the -- Increment and decrement numbers in the first row, interpreting the
-- numbers as decimal, octal or hexadecimal. -- numbers as decimal, octal or hexadecimal.
execute('set nrformats=octal,hex', '1') execute('set nrformats=bin,octal,hex', '1')
feed('102ll64128$') feed('63l102ll64128$')
-- For the second row, treat the numbers as decimal or octal. -- For the second row, treat the numbers as decimal or octal.
-- 0x100 should be interpreted as decimal 0, the character x, and decimal 100. -- 0x100 should be interpreted as decimal 0, the character x, and decimal 100.
execute('set nrformats=octal', '2') execute('set nrformats=octal', '2')
feed('0102l2w65129blx6lD') feed('0w102l2w65129blx6lD')
-- For the third row, treat the numbers as decimal or hexadecimal. -- For the third row, treat the numbers as decimal or hexadecimal.
-- 077 should be interpreted as decimal 77. -- 077 should be interpreted as decimal 77.
execute('set nrformats=hex', '3') execute('set nrformats=hex', '3')
feed('0101l257Txldt   ') feed('0101l257Txldt   ')
-- For the last row, interpret all numbers as decimal. -- For the fourth row, interpret all numbers as decimal.
execute('set nrformats=', '4') execute('set nrformats=', '4')
feed('0200l100w78') feed('0200l100w78')
-- For the last row, interpret as binary and hexadecimal.
execute('set nrformats=bin,hex', '5')
feed('010065l6432')
expect([[ expect([[
0 0x0ff 0000 -1 0b011 0 0x0ff 0000 -1
0 1x100 0777777 1b101 0 1x100 0777777
-1 0x0 078 0xFE 0xfe -1 0x0 078 0xFE 0xfe
-100 -100x100 000]]) -100 -100x100 000
0x0b0de 0b0101101]])
end) end)
end) end)

View File

@ -600,4 +600,39 @@ describe(':sort', function()
eq('Vim(sort):E474: Invalid argument', eval('tmpvar')) eq('Vim(sort):E474: Invalid argument', eval('tmpvar'))
expect(text) expect(text)
end) end)
it('binary', function()
insert([[
0b111000
0b101100
0b101001
0b101001
0b101000
0b000000
0b001000
0b010000
0b101000
0b100000
0b101010
0b100010
0b100100
0b100010]])
execute([[sort b]])
expect([[
0b000000
0b001000
0b010000
0b100000
0b100010
0b100010
0b100100
0b101000
0b101000
0b101001
0b101001
0b101010
0b101100
0b111000]])
end)
end) end)