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.
The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned
octal and hexadecimal numbers and alphabetic characters. This depends on the
'nrformats' option.
binary/octal/hexadecimal numbers and alphabetic characters. This
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'
to be octal, unless the number includes a '8' or '9'. Other numbers are
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
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),
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".
@ -397,6 +403,10 @@ octal number.
Note that when 'nrformats' includes "octal", decimal numbers with leading
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
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()|.
*: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
lines are sorted.
@ -1622,6 +1632,9 @@ found here: |sort()|, |uniq()|.
With [o] sorting is done on the first octal number in
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
identical lines (ignoring case when [i] is used).
Without this flag, a sequence of identical lines

View File

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

View File

@ -122,6 +122,14 @@ static inline bool ascii_isxdigit(int c)
|| (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,
/// one of \f, \n, \r, \t, \v.
///

View File

@ -1454,6 +1454,20 @@ char_u* skipdigits(char_u *q)
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
///
/// @param q
@ -1485,6 +1499,20 @@ char_u* skiptodigit(char_u *q)
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)
///
/// @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
/// hexadecimal and octal numbers. Accepts a '-' sign.
/// If "hexp" is not NULL, returns a flag to indicate the type of the number:
/// hexadecimal, octal and binary numbers. Accepts a '-' sign.
/// If "prep" is not NULL, returns a flag to indicate the type of the number:
/// 0 decimal
/// '0' octal
/// 'B' bin
/// 'b' bin
/// 'X' hex
/// 'x' hex
/// 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 "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
/// octal number.
/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume
/// hex number.
///
/// @param start
/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex,
// '0' = octal
/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
// '0' = octal, 'b' or 'B' is bin
/// @param len Returns the detected length of number.
/// @param dobin recognize binary number
/// @param dooct recognize octal number
/// @param dohex recognize hex number
/// @param nptr Returns the signed 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)
{
char_u *ptr = start;
int hex = 0; // default is decimal
int pre = 0; // default is decimal
int negative = FALSE;
unsigned long un = 0;
int n;
@ -1756,31 +1789,35 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
++ptr;
}
// Recognize hex and octal.
// Recognize hex, octal, and bin.
if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) {
hex = ptr[1];
pre = ptr[1];
if (dohex
&& ((hex == 'X') || (hex == 'x'))
&& ((pre == 'X') || (pre == 'x'))
&& ascii_isxdigit(ptr[2])) {
// hexadecimal
ptr += 2;
} else if (dobin
&& ((pre == 'B') || (pre == 'b'))
&& ascii_isbdigit(ptr[2])) {
// binary
ptr += 2;
} else {
// default is decimal
hex = 0;
pre = 0;
if (dooct) {
// Don't interpret "0", "08" or "0129" as octal.
for (n = 1; ascii_isdigit(ptr[n]); ++n) {
if (ptr[n] > '7') {
// can't be octal
hex = 0;
pre = 0;
break;
}
if (ptr[n] >= '0') {
// 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.
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
while ('0' <= *ptr && *ptr <= '7') {
un = 8 * un + (unsigned long)(*ptr - '0');
ptr++;
}
} else if ((hex != 0) || (dohex > 1)) {
} else if (pre != 0 || dohex > 1) {
// hex
if (pre != 0)
n += 2; // skip over "0x"
while (ascii_isxdigit(*ptr)) {
un = 16 * un + (unsigned long)hex2nr(*ptr);
ptr++;
++ptr;
}
} else {
// decimal
while (ascii_isdigit(*ptr)) {
un = 10 * un + (unsigned long)(*ptr - '0');
ptr++;
++ptr;
}
}
if (hexp != NULL) {
*hexp = hex;
if (prep != NULL) {
*prep = pre;
}
if (len != NULL) {

View File

@ -1149,7 +1149,7 @@ call_vim_function (
len = 0;
else
/* 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])) {
argvars[i].v_type = VAR_NUMBER;
argvars[i].vval.v_number = n;
@ -4127,7 +4127,7 @@ eval7 (
rettv->vval.v_float = f;
}
} else {
vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL);
vim_str2nr(*arg, NULL, &len, TRUE, TRUE, TRUE, &n, NULL);
*arg += len;
if (evaluate) {
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) {
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));
return;
}
@ -15991,7 +15991,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv)
p = skipwhite(get_tv_string(&argvars[0]));
if (*p == '+')
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;
}
@ -18273,7 +18273,7 @@ long get_tv_number_chk(typval_T *varp, int *denote)
case VAR_STRING:
if (varp->vval.v_string != NULL)
vim_str2nr(varp->vval.v_string, NULL, NULL,
TRUE, TRUE, &n, NULL);
TRUE, TRUE, TRUE, &n, NULL);
return n;
case VAR_LIST:
EMSG(_("E745: Using a List as a Number"));

View File

@ -348,6 +348,7 @@ void ex_sort(exarg_T *eap)
long deleted;
colnr_T start_col;
colnr_T end_col;
int sort_bin; /* sort on bin number */
int sort_oct; /* sort on octal number */
int sort_hex; /* sort on hex number */
@ -362,7 +363,7 @@ void ex_sort(exarg_T *eap)
regmatch.regprog = NULL;
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) {
if (ascii_iswhite(*p))
@ -373,6 +374,8 @@ void ex_sort(exarg_T *eap)
sort_rx = TRUE;
else if (*p == 'n')
sort_nr = 2;
else if (*p == 'b')
sort_bin = 2;
else if (*p == 'o')
sort_oct = 2;
else if (*p == 'x')
@ -410,14 +413,14 @@ void ex_sort(exarg_T *eap)
}
}
/* Can only have one of 'n', 'o' and 'x'. */
if (sort_nr + sort_oct + sort_hex > 2) {
/* Can only have one of 'n', 'b', 'o' and 'x'. */
if (sort_nr + sort_bin + sort_oct + sort_hex > 2) {
EMSG(_(e_invarg));
goto sortend;
}
/* 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
@ -454,6 +457,8 @@ void ex_sort(exarg_T *eap)
p = s + start_col;
if (sort_hex)
s = skiptohex(p);
else if (sort_bin)
s = skiptobin(p);
else
s = skiptodigit(p);
if (s > p && s[-1] == '-')
@ -462,7 +467,7 @@ void ex_sort(exarg_T *eap)
/* empty line should sort before any number */
nrs[lnum - eap->line1].start_col_nr = -MAXLNUM;
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);
*s2 = c;
} else {

View File

@ -4780,7 +4780,7 @@ int get_list_range(char_u **str, int *num1, int *num2)
*str = skipwhite(*str);
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;
*num1 = (int)num;
first = TRUE;
@ -4788,7 +4788,7 @@ int get_list_range(char_u **str, int *num1, int *num2)
*str = skipwhite(*str);
if (**str == ',') { /* parse "to" part of range */
*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) {
*num2 = (int)num;
*str = skipwhite(*str + len);

View File

@ -575,7 +575,7 @@ find_special_key (
if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3])
bp += 3; /* skip t_xx, xx may be '-' or '>' */
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;
break;
}
@ -602,7 +602,7 @@ find_special_key (
if (STRNICMP(last_dash + 1, "char-", 5) == 0
&& ascii_isdigit(last_dash[6])) {
/* <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;
} else {
/*

View File

@ -3037,7 +3037,7 @@ static double tv_float(typval_T *tvs, int *idxp)
* http://www.ijs.si/software/snprintf/
*
* 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 '#'.
* 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;
case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
// u, o, x, X and p conversion specifiers imply the value is unsigned;
case 'd': case 'u': case 'b': case 'B': case 'o': case 'x': case 'X': case 'p': {
// u, b, B, o, x, X and p conversion specifiers imply the value is unsigned;
// d implies a signed value
// 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
// avoid handling tricky cases like (short int)-32768
} 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++] = 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
if (precision == 0 && arg_sign == 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 {
char f[5];
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);
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 {
// unsigned
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
&& 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] == 'b'
|| tmp[zero_padding_insertion_ind + 1] == 'B'))
zero_padding_insertion_ind += 2;
}

View File

@ -4197,7 +4197,7 @@ int do_addsub(int command, linenr_T Prenum1)
int col;
char_u *buf1;
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 */
unsigned long n, oldn;
char_u *ptr;
@ -4206,6 +4206,7 @@ int do_addsub(int command, linenr_T Prenum1)
int todel;
int dohex;
int dooct;
int dobin;
int doalp;
int firstdigit;
int negative;
@ -4213,6 +4214,7 @@ int do_addsub(int command, linenr_T Prenum1)
dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */
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" */
ptr = get_cursor_line_ptr();
@ -4222,18 +4224,43 @@ int do_addsub(int command, linenr_T Prenum1)
* First check if we are on a hexadecimal number, after the "0x".
*/
col = curwin->w_cursor.col;
if (dobin)
while (col > 0 && ascii_isbdigit(ptr[col]))
--col;
if (dohex)
while (col > 0 && ascii_isxdigit(ptr[col]))
--col;
if ( dohex
if ( dobin
&& dohex
&& ! ((col > 0
&& (ptr[col] == 'X'
|| ptr[col] == 'x')
&& ptr[col - 1] == '0'
&& ascii_isxdigit(ptr[col + 1])))) {
/* In case of binary/hexadecimal pattern overlap match, rescan */
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])) {
/*
* Found hexadecimal number, move to its start.
*/
&& 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 {
/*
@ -4297,10 +4324,10 @@ int do_addsub(int command, linenr_T Prenum1)
}
/* 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 */
if (hex && negative) {
/* ignore leading '-' for hex, octal and bin numbers */
if (pre && negative) {
++col;
--length;
negative = FALSE;
@ -4320,7 +4347,7 @@ int do_addsub(int command, linenr_T Prenum1)
n += (unsigned long)Prenum1;
/* handle wraparound for decimal numbers */
if (!hex) {
if (!pre) {
if (subtract) {
if (n > oldn) {
n = 1 + (n ^ (unsigned long)-1);
@ -4370,23 +4397,38 @@ int do_addsub(int command, linenr_T Prenum1)
if (negative) {
*ptr++ = '-';
}
if (hex) {
if (pre) {
*ptr++ = '0';
--length;
}
if (hex == 'x' || hex == 'X') {
*ptr++ = hex;
if (pre == 'b' || pre == 'B'
|| pre == 'x' || pre == 'X') {
*ptr++ = pre;
--length;
}
/*
* 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);
else if (hex == '0')
else if (pre == '0')
sprintf((char *)buf2, "%" PRIo64, (uint64_t)n);
else if (hex && hexupper)
else if (pre && hexupper)
sprintf((char *)buf2, "%" PRIX64, (uint64_t)n);
else
sprintf((char *)buf2, "%" PRIx64, (uint64_t)n);
@ -4398,7 +4440,7 @@ int do_addsub(int command, linenr_T Prenum1)
* Don't do this when
* the result may look like an octal number.
*/
if (firstdigit == '0' && !(dooct && hex == 0))
if (firstdigit == '0' && !(dooct && pre == 0))
while (length-- > 0)
*ptr++ = '0';
*ptr = NUL;

View File

@ -260,7 +260,7 @@ typedef struct vimoption {
static char *(p_ambw_values[]) = {"single", "double", 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_wop_values[]) = {"tagfile", NULL};
static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
@ -1431,7 +1431,7 @@ do_set (
} else if (*arg == '-' || ascii_isdigit(*arg)) {
// Allow negative (for 'undolevels'), octal and
// 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])) {
errmsg = e_invarg;
goto skip;

View File

@ -1599,7 +1599,7 @@ return {
deny_duplicates=true,
alloced=true,
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',

View File

@ -1095,7 +1095,9 @@ spell_check (
// 0X99FF. But always do check spelling to find "3GPP" and "11
// julifeest".
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);
else
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 */
#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

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

View File

@ -600,4 +600,39 @@ describe(':sort', function()
eq('Vim(sort):E474: Invalid argument', eval('tmpvar'))
expect(text)
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)