소스 검색

no more use of 'scanf' for reading numbers

Roberto Ierusalimschy 11 년 전
부모
커밋
e966e53791
2개의 변경된 파일82개의 추가작업 그리고 27개의 파일을 삭제
  1. 81 17
      liolib.c
  2. 1 10
      luaconf.h

+ 81 - 17
liolib.c

@@ -1,5 +1,5 @@
 /*
-** $Id: liolib.c,v 2.123 2014/05/13 19:40:28 roberto Exp roberto $
+** $Id: liolib.c,v 2.124 2014/05/15 15:21:06 roberto Exp $
 ** Standard I/O (and system) library
 ** See Copyright Notice in lua.h
 */
@@ -15,7 +15,9 @@
 #endif
 
 
+#include <ctype.h>
 #include <errno.h>
+#include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -361,26 +363,91 @@ static int io_lines (lua_State *L) {
 */
 
 
-static int read_integer (lua_State *L, FILE *f) {
-  lua_Integer d;
-  if (fscanf(f, LUA_INTEGER_SCAN, &d) == 1) {
-    lua_pushinteger(L, d);
-    return 1;
-  }
+/* maximum length of a numeral */
+#define MAXRN		200
+
+/* auxiliary structure used by 'read_number' */
+typedef struct {
+  FILE *f;  /* file being read */
+  int c;  /* current character (look ahead) */
+  int n;  /* number of elements in buffer 'buff' */
+  char buff[MAXRN];
+} RN;
+
+
+/*
+** Add current char to buffer (if not out of space) and read next one
+*/
+static int nextc (RN *rn) {
+  if (rn->n >= MAXRN)  /* buffer overflow? */
+    return 0;  /* fail */
   else {
-   lua_pushnil(L);  /* "result" to be removed */
-   return 0;  /* read fails */
+    rn->buff[rn->n++] = rn->c;  /* save current char */
+    rn->c = l_getc(rn->f);  /* read next one */
+    return 1;
   }
 }
 
 
+/*
+** Accept current char if it is in 'set' (of size 1 or 2)
+*/
+static int test2 (RN *rn, const char *set) {
+  if (rn->c == set[0] || (rn->c == set[1] && rn->c != '\0'))
+    return nextc(rn);
+  else return 0;
+}
+
+
+/*
+** Read a sequence of (hexa)digits
+*/
+static int readdigits (RN *rn, int hexa) {
+  int count = 0;
+  while ((hexa ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
+    count++;
+  return count;
+}
+
+
+/* access to locale "radix character" (decimal point) */
+#if !defined(getlocaledecpoint)
+#define getlocaledecpoint()     (localeconv()->decimal_point[0])
+#endif
+
+
+/*
+** Read a number: first reads a valid prefix of a numeral into a buffer.
+** Then it calls 'lua_strtonum' to check whether the format is correct
+** and to convert it to a Lua number
+*/
 static int read_number (lua_State *L, FILE *f) {
-  lua_Number d;
-  if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
-    lua_pushnumber(L, d);
-    return 1;
+  RN rn;
+  int count = 0;
+  int hexa = 0;
+  char decp[2] = ".";
+  rn.f = f; rn.n = 0;
+  decp[0] = getlocaledecpoint();  /* get decimal point from locale */
+  l_lockfile(rn.f);
+  do { rn.c = l_getc(rn.f); } while (isspace(rn.c));  /* skip spaces */
+  test2(&rn, "-+");  /* optional signal */
+  if (test2(&rn, "0")) {
+    if (test2(&rn, "xX")) hexa = 1;  /* numeral is hexadecimal */
+    else count = 1;  /* count initial '0' as a valid digit */
   }
-  else {
+  count += readdigits(&rn, hexa);  /* integral part */
+  if (test2(&rn, decp))  /* decimal point? */
+    count += readdigits(&rn, hexa);  /* fractionary part */
+  if (count > 0 && test2(&rn, (hexa ? "pP" : "eE"))) {  /* exponent mark? */
+    test2(&rn, "-+");  /* exponent signal */
+    readdigits(&rn, 0);  /* exponent digits */
+  }
+  ungetc(rn.c, rn.f);  /* unread look-ahead char */
+  l_unlockfile(rn.f);
+  rn.buff[rn.n] = '\0';  /* finish string */
+  if (lua_strtonum(L, rn.buff))  /* is this a valid number? */
+    return 1;  /* ok */
+  else {  /* invalid format */
    lua_pushnil(L);  /* "result" to be removed */
    return 0;  /* read fails */
   }
@@ -456,9 +523,6 @@ static int g_read (lua_State *L, FILE *f, int first) {
         const char *p = luaL_checkstring(L, n);
         if (*p == '*') p++;  /* skip optional '*' (for compatibility) */
         switch (*p) {
-          case 'i':  /* integer */
-            success = read_integer(L, f);
-            break;
           case 'n':  /* number */
             success = read_number(L, f);
             break;

+ 1 - 10
luaconf.h

@@ -1,5 +1,5 @@
 /*
-** $Id: luaconf.h,v 1.201 2014/05/11 13:03:48 roberto Exp roberto $
+** $Id: luaconf.h,v 1.202 2014/05/15 15:24:32 roberto Exp roberto $
 ** Configuration file for Lua
 ** See Copyright Notice in lua.h
 */
@@ -431,7 +431,6 @@
 @@ over a floating number.
 **
 @@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
-@@ LUA_NUMBER_SCAN is the format for reading floats.
 @@ LUA_NUMBER_FMT is the format for writing floats.
 @@ lua_number2str converts a float to a string.
 **
@@ -447,7 +446,6 @@
 #define LUAI_UACNUMBER	double
 
 #define LUA_NUMBER_FRMLEN	""
-#define LUA_NUMBER_SCAN		"%f"
 #define LUA_NUMBER_FMT		"%.7g"
 
 #define l_mathop(op)		op##f
@@ -462,7 +460,6 @@
 #define LUAI_UACNUMBER	long double
 
 #define LUA_NUMBER_FRMLEN	"L"
-#define LUA_NUMBER_SCAN		"%Lf"
 #define LUA_NUMBER_FMT		"%.19Lg"
 
 #define l_mathop(op)		op##l
@@ -476,7 +473,6 @@
 #define LUAI_UACNUMBER	double
 
 #define LUA_NUMBER_FRMLEN	""
-#define LUA_NUMBER_SCAN		"%lf"
 #define LUA_NUMBER_FMT		"%.14g"
 
 #define l_mathop(op)		op
@@ -552,7 +548,6 @@
 @@ LUAI_UACINT is the result of an 'usual argument conversion'
 @@ over a lUA_INTEGER.
 @@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
-@@ LUA_INTEGER_SCAN is the format for reading integers.
 @@ LUA_INTEGER_FMT is the format for writing integers.
 @@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
 @@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
@@ -563,7 +558,6 @@
 
 /* The following definitions are good for most cases here */
 
-#define LUA_INTEGER_SCAN	"%" LUA_INTEGER_FRMLEN "d"
 #define LUA_INTEGER_FMT		"%" LUA_INTEGER_FRMLEN "d"
 #define lua_integer2str(s,n)	sprintf((s), LUA_INTEGER_FMT, (n))
 
@@ -634,9 +628,6 @@
 #undef  LUAI_UACINT
 #define LUAI_UACINT		int
 
-#undef  LUA_INTEGER_SCAN
-#define LUA_INTEGER_SCAN	"%hd"
-
 #undef  LUAI_MAXSTACK
 #define LUAI_MAXSTACK           15000