charset.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #include <stdbool.h>
  2. #include "nvim/ascii.h"
  3. #include "nvim/macros.h"
  4. #include "nvim/charset.h"
  5. #include "nvim/eval/typval.h"
  6. #include "nvim/vim.h"
  7. int hex2nr(int c)
  8. {
  9. if ((c >= 'a') && (c <= 'f')) {
  10. return c - 'a' + 10;
  11. }
  12. if ((c >= 'A') && (c <= 'F')) {
  13. return c - 'A' + 10;
  14. }
  15. return c - '0';
  16. }
  17. void vim_str2nr(const char_u *const start, int *const prep, int *const len,
  18. const int what, varnumber_T *const nptr,
  19. uvarnumber_T *const unptr, const int maxlen)
  20. {
  21. const char *ptr = (const char *)start;
  22. #define STRING_ENDED(ptr) \
  23. (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
  24. int pre = 0; // default is decimal
  25. const bool negative = (ptr[0] == '-');
  26. uvarnumber_T un = 0;
  27. if (negative) {
  28. ptr++;
  29. }
  30. if (what & STR2NR_FORCE) {
  31. // When forcing main consideration is skipping the prefix. Octal and decimal
  32. // numbers have no prefixes to skip. pre is not set.
  33. switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) {
  34. case STR2NR_HEX: {
  35. if (!STRING_ENDED(ptr + 2)
  36. && ptr[0] == '0'
  37. && (ptr[1] == 'x' || ptr[1] == 'X')
  38. && ascii_isxdigit(ptr[2])) {
  39. ptr += 2;
  40. }
  41. goto vim_str2nr_hex;
  42. }
  43. case STR2NR_BIN: {
  44. if (!STRING_ENDED(ptr + 2)
  45. && ptr[0] == '0'
  46. && (ptr[1] == 'b' || ptr[1] == 'B')
  47. && ascii_isbdigit(ptr[2])) {
  48. ptr += 2;
  49. }
  50. goto vim_str2nr_bin;
  51. }
  52. case STR2NR_OCT: {
  53. goto vim_str2nr_oct;
  54. }
  55. case 0: {
  56. goto vim_str2nr_dec;
  57. }
  58. default: {
  59. abort();
  60. }
  61. }
  62. } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
  63. && !STRING_ENDED(ptr + 1)
  64. && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
  65. pre = ptr[1];
  66. // Detect hexadecimal: 0x or 0X followed by hex digit
  67. if ((what & STR2NR_HEX)
  68. && !STRING_ENDED(ptr + 2)
  69. && (pre == 'X' || pre == 'x')
  70. && ascii_isxdigit(ptr[2])) {
  71. ptr += 2;
  72. goto vim_str2nr_hex;
  73. }
  74. // Detect binary: 0b or 0B followed by 0 or 1
  75. if ((what & STR2NR_BIN)
  76. && !STRING_ENDED(ptr + 2)
  77. && (pre == 'B' || pre == 'b')
  78. && ascii_isbdigit(ptr[2])) {
  79. ptr += 2;
  80. goto vim_str2nr_bin;
  81. }
  82. // Detect octal number: zero followed by octal digits without '8' or '9'
  83. pre = 0;
  84. if (!(what & STR2NR_OCT)) {
  85. goto vim_str2nr_dec;
  86. }
  87. for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
  88. if (ptr[i] > '7') {
  89. goto vim_str2nr_dec;
  90. }
  91. }
  92. pre = '0';
  93. goto vim_str2nr_oct;
  94. } else {
  95. goto vim_str2nr_dec;
  96. }
  97. // Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
  98. abort(); // Should’ve used goto earlier.
  99. #define PARSE_NUMBER(base, cond, conv) \
  100. do { \
  101. while (!STRING_ENDED(ptr) && (cond)) { \
  102. /* avoid ubsan error for overflow */ \
  103. if (un < UVARNUMBER_MAX / base) { \
  104. un = base * un + (uvarnumber_T)(conv); \
  105. } else { \
  106. un = UVARNUMBER_MAX; \
  107. } \
  108. ptr++; \
  109. } \
  110. } while (0)
  111. switch (pre) {
  112. case 'b':
  113. case 'B': {
  114. vim_str2nr_bin:
  115. PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
  116. break;
  117. }
  118. case '0': {
  119. vim_str2nr_oct:
  120. PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
  121. break;
  122. }
  123. case 0: {
  124. vim_str2nr_dec:
  125. PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
  126. break;
  127. }
  128. case 'x':
  129. case 'X': {
  130. vim_str2nr_hex:
  131. PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
  132. break;
  133. }
  134. }
  135. #undef PARSE_NUMBER
  136. if (prep != NULL) {
  137. *prep = pre;
  138. }
  139. if (len != NULL) {
  140. *len = (int)(ptr - (const char *)start);
  141. }
  142. if (nptr != NULL) {
  143. if (negative) { // account for leading '-' for decimal numbers
  144. // avoid ubsan error for overflow
  145. if (un > VARNUMBER_MAX) {
  146. *nptr = VARNUMBER_MIN;
  147. } else {
  148. *nptr = -(varnumber_T)un;
  149. }
  150. } else {
  151. if (un > VARNUMBER_MAX) {
  152. un = VARNUMBER_MAX;
  153. }
  154. *nptr = (varnumber_T)un;
  155. }
  156. }
  157. if (unptr != NULL) {
  158. *unptr = un;
  159. }
  160. #undef STRING_ENDED
  161. }