new scanf implementation and corresponding integer parser/converter
[musl] / src / internal / intscan.c
diff --git a/src/internal/intscan.c b/src/internal/intscan.c
new file mode 100644 (file)
index 0000000..a00f2cc
--- /dev/null
@@ -0,0 +1,97 @@
+#include <limits.h>
+#include <errno.h>
+#include "shgetc.h"
+
+/* Lookup table for digit values. -1==255>=36 -> invalid */
+static const unsigned char table[] = { -1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
+-1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
+25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
+-1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
+25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+};
+
+unsigned long long __intscan(FILE *f, unsigned base, int pok, unsigned long long lim)
+{
+       const unsigned char *val = table+1;
+       int c, neg=0;
+       unsigned x;
+       unsigned long long y;
+       if (base > 36) {
+               errno = EINVAL;
+               return 0;
+       }
+       c = shgetc(f);
+       if (c=='+' || c=='-') {
+               neg = -(c=='-');
+               c = shgetc(f);
+       }
+       if ((base == 0 || base == 16) && c=='0') {
+               c = shgetc(f);
+               if ((c|32)=='x') {
+                       c = shgetc(f);
+                       if (val[c]>=16) {
+                               shunget(f);
+                               if (pok) shunget(f);
+                               else shlim(f, 0);
+                               return 0;
+                       }
+                       base = 16;
+               } else if (base == 0) {
+                       base = 8;
+               }
+       } else {
+               if (base == 0) base = 10;
+               if (val[c] >= base) {
+                       shlim(f, 0);
+                       errno = EINVAL;
+                       return 0;
+               }
+       }
+       if (base == 10) {
+               for (x=0; c-'0'<10U && x<=UINT_MAX/10-1; c=shgetc(f))
+                       x = x*10 + (c-'0');
+               for (y=x; c-'0'<10U && y<=ULLONG_MAX/10 && 10*y<=ULLONG_MAX-(c-'0'); c=shgetc(f))
+                       y = y*10 + (c-'0');
+               if (c-'0'>=10U) goto done;
+       } else if (!(base & base-1)) {
+               int bs = "\0\1\2\4\7\3\6\5"[(0x17*base)>>5&7];
+               for (x=0; val[c]<base && x<=UINT_MAX/32; c=shgetc(f))
+                       x = x<<bs | val[c];
+               for (y=x; val[c]<base && y<=ULLONG_MAX>>bs; c=shgetc(f))
+                       y = y<<bs | val[c];
+       } else {
+               for (x=0; val[c]<base && x<=UINT_MAX/36-1; c=shgetc(f))
+                       x = x*base + val[c];
+               for (y=x; val[c]<base && y<=ULLONG_MAX/base && base*y<=ULLONG_MAX-val[c]; c=shgetc(f))
+                       y = y*base + val[c];
+       }
+       if (val[c]<base) {
+               for (; val[c]<base; c=shgetc(f));
+               errno = ERANGE;
+               y = lim;
+       }
+done:
+       shunget(f);
+       if (y>=lim) {
+               if (!(lim&1) && !neg) {
+                       errno = ERANGE;
+                       return lim-1;
+               } else if (y>lim) {
+                       errno = ERANGE;
+                       return lim;
+               }
+       }
+       return (y^neg)-neg;
+}