Add support for hex floats to *scanf

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Add support for hex floats to *scanf

Michael Forney
I noticed that OpenBSD's fscanf doesn't yet support hex float strings,
which are standardized in C99. I am using them in my application (which
I would like to support OpenBSD), since the "%a" format specifier is a
convenient way to preserve the exact floating point value.

strtod already supports parsing hex floats, so it is just the scanner
in __svfscanf that needed changes.

The implementation reuses the PFXOK and NZDIGITS flags from CT_INT
scanning and follows similar logic to CT_INT. This required allocating
new flag values for DPTOK and EXPOK.

I did my best to follow style(9), but since the indentation level of this
switch is so high, I found it difficult wrap lines nicely. I noticed that
several existing lines broke the "space around binary operators" rule if
the added space would require unnatural wrapping, so I did the same here.

I wasn't sure which comments I should carry over from the CT_INT case
(for example, above `case 'x':`), or if any of the other changes require
additional comments. Please let me know if they do.

diff --git lib/libc/stdio/vfscanf.c lib/libc/stdio/vfscanf.c
index 5fb55d99e61..87134a9ef86 100644
--- lib/libc/stdio/vfscanf.c
+++ lib/libc/stdio/vfscanf.c
@@ -66,19 +66,19 @@
 
 /*
  * The following are used in numeric conversions only:
- * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
- * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
+ * DPTOK and EXPOK are for floating point;
+ * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral and floating
+ * point.
  */
 #define SIGNOK 0x01000 /* +/- is (still) legal */
 #define HAVESIGN 0x02000 /* sign detected */
 #define NDIGITS 0x04000 /* no digits detected */
-
-#define DPTOK 0x08000 /* (float) decimal point is still legal */
-#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
-
 #define PFXOK 0x08000 /* 0x prefix is (still) legal */
 #define NZDIGITS 0x10000 /* no zero digits detected */
 
+#define DPTOK 0x20000 /* (float) decimal point is still legal */
+#define EXPOK 0x40000 /* (float) exponent (e+3, etc) still legal */
+
 /*
  * Conversion types.
  */
@@ -770,7 +770,8 @@ literal:
  width = sizeof(buf) - 2;
  width++;
 #endif
- flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
+ flags |= SIGNOK | NDIGITS | NZDIGITS | DPTOK | EXPOK;
+ base = 10;
  for (p = buf; width; width--) {
  c = *fp->_p;
  /*
@@ -779,15 +780,36 @@ literal:
  */
  switch (c) {
 
- case '0': case '1': case '2': case '3':
+ case '0':
+ if ((flags&(NZDIGITS|NDIGITS|DPTOK)) ==
+    (NZDIGITS|NDIGITS|DPTOK))
+ flags |= PFXOK;
+ else
+ flags &= ~PFXOK;
+ flags &=
+    ~(SIGNOK | NZDIGITS | NDIGITS);
+ goto fok;
+
+ case '1': case '2': case '3':
  case '4': case '5': case '6': case '7':
  case '8': case '9':
- flags &= ~(SIGNOK | NDIGITS);
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+ goto fok;
+
+ /* letters ok iff hex */
+ case 'A': case 'B': case 'C':
+ case 'D': case 'F':
+ case 'a': case 'b': case 'c':
+ case 'd': case 'f':
+ if (base == 10)
+ break;  /* not legal here */
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
  goto fok;
 
  case '+': case '-':
  if (flags & SIGNOK) {
  flags &= ~SIGNOK;
+ flags |= HAVESIGN;
  goto fok;
  }
  break;
@@ -799,11 +821,35 @@ literal:
  break;
  case 'e': case 'E':
  /* no exponent without some digits */
- if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
+ if (base == 10 &&
+    (flags&(NDIGITS|EXPOK)) == EXPOK) {
  flags =
     (flags & ~(EXPOK|DPTOK)) |
     SIGNOK | NDIGITS;
  goto fok;
+ } else if (base == 16) {
+ flags &=
+    ~(SIGNOK|PFXOK|NDIGITS);
+ goto fok;
+ }
+ break;
+ case 'p': case 'P':
+ /* no exponent without some digits */
+ if (base == 16 &&
+    (flags&(NDIGITS|EXPOK)) == EXPOK) {
+ flags =
+    (flags & ~(EXPOK|DPTOK)) |
+    SIGNOK | NDIGITS;
+ base = 10;
+ goto fok;
+ }
+ break;
+ case 'x': case 'X':
+ if ((flags & PFXOK) && p ==
+    buf + 1 + !!(flags & HAVESIGN)) {
+ base = 16;
+ flags &= ~PFXOK;
+ goto fok;
  }
  break;
  }

Reply | Threaded
Open this post in threaded view
|

Re: Add support for hex floats to *scanf

Michael Forney
On 2019-05-28, Michael Forney <[hidden email]> wrote:

> I noticed that OpenBSD's fscanf doesn't yet support hex float strings,
> which are standardized in C99. I am using them in my application (which
> I would like to support OpenBSD), since the "%a" format specifier is a
> convenient way to preserve the exact floating point value.
>
> strtod already supports parsing hex floats, so it is just the scanner
> in __svfscanf that needed changes.
>
> The implementation reuses the PFXOK and NZDIGITS flags from CT_INT
> scanning and follows similar logic to CT_INT. This required allocating
> new flag values for DPTOK and EXPOK.
>
> I did my best to follow style(9), but since the indentation level of this
> switch is so high, I found it difficult wrap lines nicely. I noticed that
> several existing lines broke the "space around binary operators" rule if
> the added space would require unnatural wrapping, so I did the same here.
>
> I wasn't sure which comments I should carry over from the CT_INT case
> (for example, above `case 'x':`), or if any of the other changes require
> additional comments. Please let me know if they do.

Just bumping this so it doesn't get forgotten. I'd be happy to address
any feedback anyone might have.

Reply | Threaded
Open this post in threaded view
|

Re: Add support for hex floats to *scanf

Ori Bernstein
>> I wasn't sure which comments I should carry over from the CT_INT case
>> (for example, above `case 'x':`), or if any of the other changes require
>> additional comments. Please let me know if they do.
>
> Just bumping this so it doesn't get forgotten. I'd be happy to address
> any feedback anyone might have.

The looks good to me.  I've managed to destroy my main computer, and
am waiting for replacement hardware to ship -- but if nobody has
objections, I'll commit as soon as I get things back up.

ok orib@

Reply | Threaded
Open this post in threaded view
|

Re: Add support for hex floats to *scanf

Michael Forney
In reply to this post by Michael Forney
On 2020-01-28, Michael Forney <[hidden email]> wrote:

> On 2019-05-28, Michael Forney <[hidden email]> wrote:
>> I noticed that OpenBSD's fscanf doesn't yet support hex float strings,
>> which are standardized in C99. I am using them in my application (which
>> I would like to support OpenBSD), since the "%a" format specifier is a
>> convenient way to preserve the exact floating point value.
>>
>> strtod already supports parsing hex floats, so it is just the scanner
>> in __svfscanf that needed changes.
>>
>> The implementation reuses the PFXOK and NZDIGITS flags from CT_INT
>> scanning and follows similar logic to CT_INT. This required allocating
>> new flag values for DPTOK and EXPOK.
>>
>> I did my best to follow style(9), but since the indentation level of this
>> switch is so high, I found it difficult wrap lines nicely. I noticed that
>> several existing lines broke the "space around binary operators" rule if
>> the added space would require unnatural wrapping, so I did the same here.
>>
>> I wasn't sure which comments I should carry over from the CT_INT case
>> (for example, above `case 'x':`), or if any of the other changes require
>> additional comments. Please let me know if they do.
>
> Just bumping this so it doesn't get forgotten. I'd be happy to address
> any feedback anyone might have.

Ping.