delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2012/12/22/13:54:05

X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f
X-Recipient: djgpp-workers AT delorie DOT com
X-Authenticated: #27081556
X-Provags-ID: V01U2FsdGVkX1/aFb9mXFHazZ7gC4jYNh+46jT4z8SL9wYEYLMPob
bsZEhkzxnqO6zi
Message-ID: <50D5FEB0.6000000@gmx.de>
Date: Sat, 22 Dec 2012 19:40:48 +0100
From: Juan Manuel Guerrero <juan DOT guerrero AT gmx DOT de>
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:16.0) Gecko/20121025 Thunderbird/16.0.2
MIME-Version: 1.0
To: djgpp-workers AT delorie DOT com
Subject: Re: Implementation of %n$ conversion specifier for scanf family of
functions.
References: <50D4FBCF DOT 2060706 AT gmx DOT de> <8338yyqyod DOT fsf AT gnu DOT org>
In-Reply-To: <8338yyqyod.fsf@gnu.org>
X-Y-GMX-Trusted: 0
Reply-To: djgpp-workers AT delorie DOT com

Am 22.12.2012 09:27, schrieb Eli Zaretskii:
 > > +@findex _doscan AT r{, and @acronym{C99} numeric conversion specifiers}
 > > +@findex scanf AT r{, and @acronym{C99} numeric conversion specifiers}
 > > +The @code{%n$} numeric conversion specifiers are now supported
 > > +by @code{_doscan} and the @code{scanf} family of functions.
 >
 > The use of "n" in "%n$" may mislead the reader to think it's a literal
 > "n" meant here.  I suggest to use @var{n} instead, or even something
 > like @var{param-no}.
[snip]

I have changed the patch as you suggested neither less I have retained "n"
as variable name.  Keeping the variable name shall make the djgpp description
match the linux scanf man page description and the descriptio given in
<http://pubs.opengroup.org/onlinepubs/009695399/functions/scanf.html>


 > > +/* Test the n$ numeric conversion specifier.  */
[snip]
 > It would be better to include the expected results in the program,
 > instead of relying on a human eye to detect errors.

Fixed.

Regards,
Juan M. Guerrero



2012-12-22  Juan Manuel Guerrero <juan DOT guerrero AT gmx DOT de>

     * djgpp/src/docs/kb/wc204.txi:  Info about %n$ conversion specifier added.

     * djgpp/src/libc/ansi/stdio/doscan.c:  Support for %n$ conversion specifier added.

     * djgpp/src/libc/ansi/stdio/scanf.txh:  Info about %n$ conversion specifier added.

     * djgpp/tests/libc/ansi/stdio/makefile:  Added new test file tscanf3.c.

     * djgpp/tests/libc/ansi/stdio/tscanf3.c:  Test for %n$ conversion specifier.






diff -aprNU5 djgpp.orig/src/docs/kb/wc204.txi djgpp/src/docs/kb/wc204.txi
--- djgpp.orig/src/docs/kb/wc204.txi    2012-12-22 11:57:16 +0000
+++ djgpp/src/docs/kb/wc204.txi    2012-12-22 17:54:40 +0000
@@ -1261,5 +1261,10 @@ family of functions.
  @findex scanf AT r{, and m modifier character}
  The @code{m} modifier character is now supported by @code{_doscan}
  and the @code{scanf} family of functions.  This is a @acronym{GNU}
  @code{glibc} extension and it is specified in the upcoming revision
  of the @acronym{POSIX.1} standard.
+
+@findex _doscan AT r{, and @acronym{C99} numeric conversion specifiers}
+@findex scanf AT r{, and @acronym{C99} numeric conversion specifiers}
+The @code{%@var{n}$} numeric conversion specifiers are now supported
+by @code{_doscan} and the @code{scanf} family of functions.
diff -aprNU5 djgpp.orig/src/libc/ansi/stdio/doscan.c djgpp/src/libc/ansi/stdio/doscan.c
--- djgpp.orig/src/libc/ansi/stdio/doscan.c    2012-12-22 11:57:16 +0000
+++ djgpp/src/libc/ansi/stdio/doscan.c    2012-12-22 17:54:40 +0000
@@ -61,15 +61,17 @@ _doscan(FILE *iop, const char *fmt, va_l

  int
  _doscan_low(FILE *iop, int (*scan_getc)(FILE *), int (*scan_ungetc)(int, FILE *), const char *fmt, va_list argp)
  {
    register int ch;
-  int nmatch, len, ch1;
+  int nmatch, n, len, ch1;
    int *ptr, fileended, size;
    int suppressed;
    bool allocate_char_buffer;
    int previous_errno = errno;
+  const va_list arg_list = argp;
+  bool retrieve_arg_ptr;

    decimal_point = localeconv()->decimal_point[0];
    nchars = 0;
    nmatch = 0;
    fileended = 0;
@@ -84,25 +86,37 @@ _doscan_low(FILE *iop, int (*scan_getc)(
        return nmatch;
      case '%':
        if ((ch = *fmt++) == '%')
          goto def;

+      retrieve_arg_ptr = true;
        allocate_char_buffer = false;
        ptr = NULL;
-      if (ch != '*')
+repeat:
+      if (ch != '*' && retrieve_arg_ptr)
          ptr = va_arg(argp, int *);
        else
          ch = *fmt++;
-      len = 0;
+      n = len = 0;
        size = REGULAR;
        while (isdigit(ch & 0xff))
        {
-        len = len * 10 + ch - '0';
+        n = n * 10 + ch - '0';
          ch = *fmt++;
        }
-      if (len == 0)
-        len = DEFAULT_WIDTH;
+      if (ch == '$')
+      {
+        /* C99 */
+        /* for %n$ numeric conversion specifier */
+        int i;
+        for (argp = arg_list, i = 0; i < n; i++)
+          ptr = va_arg(argp, int *);
+        retrieve_arg_ptr = false;
+        goto repeat;
+      }
+      else
+        len = (n == 0) ? DEFAULT_WIDTH : n;

        if (ch == 'l')
        {
          size = LONG;
          ch = *fmt++;
diff -aprNU5 djgpp.orig/src/libc/ansi/stdio/scanf.txh djgpp/src/libc/ansi/stdio/scanf.txh
--- djgpp.orig/src/libc/ansi/stdio/scanf.txh    2012-12-22 11:57:16 +0000
+++ djgpp/src/libc/ansi/stdio/scanf.txh    2012-12-22 18:06:50 +0000
@@ -16,12 +16,27 @@ the variables pointed to by the argument
  The format string contains regular characters which much match the input
  exactly as well as a conversion specifiers, which begin with a percent
  symbol.  Any whitespace in the format string matches zero or more of any
  whitespace characters in the input.  Thus, a single space may match a
  newline and two tabs in the input.  All conversions except @samp{c} and
-@samp{[} also skip leading whitespace automatically.  Each conversion
-specifier contains the following fields:
+@samp{[} also skip leading whitespace automatically.
+Conversions can be applied to the @var{n}th argument after the format in
+the argument list, rather than to the next unused argument.  In this case,
+the conversion specifier character @code{%} is replaced by the sequence
+@code{%@var{n}$}, where @var{n} is a decimal integer in the range [@code{1}, @code{number of the last argument}].
+This feature provides for the definition of format strings that select
+arguments in an order appropriate to specific languages.  In format strings
+containing the @code{%@var{n}$} form of conversion specifications, it is unspecified
+whether numbered arguments in the argument list can be referenced from
+the format string more than once.  The format can contain either form of
+a conversion specification --- that is, @code{%} or @code{%@var{n}$} ---
+but the two forms cannot be mixed within a single format string. The only
+exception to this is that @code{%%} or @code{%*} can be mixed with the
+@code{%@var{n}$} form.  When numbered argument specifications are used,
+specifying the @var{n}th argument requires that all the leading arguments,
+from the first to the (@var{n-1})th, are pointers.
+Each conversion specifier contains the following fields:

  @itemize @bullet

  @item An asterisk (@samp{*}) which indicates that the input should be
  converted according to the conversion spec, but not stored anywhere.
diff -aprNU5 djgpp.orig/tests/libc/ansi/stdio/makefile djgpp/tests/libc/ansi/stdio/makefile
--- djgpp.orig/tests/libc/ansi/stdio/makefile    2012-12-22 11:57:16 +0000
+++ djgpp/tests/libc/ansi/stdio/makefile    2012-12-22 17:54:40 +0000
@@ -23,9 +23,10 @@ SRC += sscanf2.c
  SRC += sscanf3.c
  SRC += tmpnam.c
  SRC += tremove.c
  SRC += tscanf.c
  SRC += tscanf2.c
+SRC += tscanf3.c
  SRC += tsnprtf.c
  SRC += tsnprtf2.c

  include $(TOP)/../makefile.inc
diff -aprNU5 djgpp.orig/tests/libc/ansi/stdio/tscanf3.c djgpp/tests/libc/ansi/stdio/tscanf3.c
--- djgpp.orig/tests/libc/ansi/stdio/tscanf3.c    1970-01-01 00:00:00 +0000
+++ djgpp/tests/libc/ansi/stdio/tscanf3.c    2012-12-22 17:54:40 +0000
@@ -0,0 +1,99 @@
+/* Test the n$ numeric conversion specifier.  */
+
+#include <stdio.h>
+#include <string.h>
+
+int main(void)
+{
+  const char *buffer[3] = {
+    "Fecha y hora es: 21 de diciembre de 2012, 01:02:03",
+    "Datum und Uhrzeit ist: 21. Dezember 2012, 01:02:03",
+    "Date and hour is: december 21 2012, 01:02:03"
+  };
+  const char *format[3] = {
+    "%*[A-Za-z :] %3$d %*[a-z] %2$9c %*[a-z] %1$d %*[,] %4$d%*[:] %5$d%*[:] %6$d%*",
+    "%*[A-Za-z :] %3$d %*[.] %2$8c %1$d %*[,] %4$d%*[:] %5$d%*[:] %6$d%*",
+    "%*[A-Za-z ]%*[:] %2$8c %3$d %1$d %*[,] %4$d%*[:] %5$d%*[:] %6$d%*"
+  };
+  const char *language[3] = {
+    "spanish",
+    "german",
+    "english"
+  };
+  typedef struct {
+    const int year;
+    const char *month;
+    const int day;
+    const int hour;
+    const int min;
+    const int sec;
+  } result;
+  result testcases[3] = {
+    { 2012, "diciembre", 21, 1, 2, 3},  /* spanish */
+    { 2012, "Dezember", 21, 1, 2, 3},   /* german */
+    { 2012, "december", 21, 1, 2, 3}    /* english */
+  };
+  char month[10];
+  int year, day, hour, min, sec, i, status;
+
+
+  for (status = i = 0; i < (sizeof buffer / sizeof buffer[0]);)
+  {
+    sscanf(buffer[i], format[i], &year, month, &day, &hour, &min, &sec);
+    if (i == 0)
+      month[9] = '\0';
+    else
+      month[8] = '\0';
+
+    if (year == testcases[i].year)
+    {
+      status++;
+      if (!strcmp(month, testcases[i].month))
+      {
+        status++;
+        if (day == testcases[i].day)
+        {
+          status++;
+          if (hour == testcases[i].hour)
+          {
+            status++;
+            if (min == testcases[i].min)
+            {
+              status++;
+              if (sec == testcases[i].sec)
+                status++;
+              else
+                printf("At least one conversion failed.\nConversion should have been: %d but is %d\n", testcases[i].sec, sec);
+            }
+            else
+              printf("At least one conversion failed.\nConversion should have been: %d but is %d\n", testcases[i].min, min);
+          }
+          else
+            printf("At least one conversion failed.\nConversion should have been: %d but is %d\n", testcases[i].hour, hour);
+        }
+        else
+          printf("At least one conversion failed.\nConversion should have been: %d but is %d\n", testcases[i].day, day);
+      }
+      else
+        printf("At least one conversion failed.\nConversion should have been: %s but is %s\n", testcases[i].month, month);
+    }
+    else
+      printf("At least one conversion failed.\nConversion should have been: %d but is %d\n", testcases[i].year, year);
+
+    printf("Result of scanf using the %s format string\n\"%s\":\n"
+           "  year:  %d\n"
+           "  month: %s\n"
+           "  day:   %d\n"
+           "  hour:  %d\n"
+           "  min:   %d\n"
+           "  sec:   %d\n\n", language[i], format[i], year, month, day, hour, min, sec);
+
+    if (status != 6 * ++i)
+      return status;
+  }
+
+  if (status)
+    printf("Test passed successfully.");
+
+  return 0;
+}

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019