delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2001/05/20/16:00:53

Sender: rich AT phekda DOT freeserve DOT co DOT uk
Message-ID: <3B081813.B83F96D3@phekda.freeserve.co.uk>
Date: Sun, 20 May 2001 20:16:36 +0100
From: Richard Dawe <rich AT phekda DOT freeserve DOT co DOT uk>
X-Mailer: Mozilla 4.77 [en] (X11; U; Linux 2.2.17 i586)
X-Accept-Language: de,fr
MIME-Version: 1.0
To: DJGPP workers <djgpp-workers AT delorie DOT com>
Subject: Re: snprintf?
References: <Pine DOT SUN DOT 3 DOT 91 DOT 1010520182619 DOT 23429H-100000 AT is>
Reply-To: djgpp-workers AT delorie DOT com

Hello.

Eli Zaretskii wrote:
> 
> On Sun, 20 May 2001, Richard Dawe wrote:
> > This is already handled in the patch snprintf-20001119.diff, which I
> > included in a mail called "snprintf() diff, take 4?", sent on
> > 2000-11-19:
> 
> Sorry I missed that.

No worries - it was tucked away.

> > >   - test the case of errors in format conversions (anything that
> > >     causes _doprnt return -1).
> >
> > I've looked at the _doprnt() code and I am unsure how to force a
> > format conversion error. Any suggestions?
> 
> Just force _doprint to return -1, it doesn't matter how.  Cheat by
> replacing _doprint with some fake function, if you have too.
> The objective is to see how does snprintf cope with that situation.

I took you too literally. I added a test that stubs out _doprnt() with one
that fails, as you suggested.

> > I just added a couple of tests for padding & precision specifiers and
> > that's turned up a couple of problems that I haven't investigated yet.
> 
> You can find a large sample of formats and expected results in
> djtst203.zip, in the Cygnus test suite.

It actually turned out that my format string was wrong (phew!). I added a
couple of tests to check that the buffer was not overflowed for cases
where field widths and precisions were used.

Anyhow, please find below a diff against latest CVS sources with these
changes.

The new width tests are at the end of tests/libc/ansi/stdio/tsnprtf.c; the
_doprntf() fail test is tests/libc/ansi/stdio/tsnprtf2.c. I also made
vsnprintf() zero the FILE structure before filling it in.

OK to commit?

Thanks, bye, Rich =]

-- 
Richard Dawe
http://www.phekda.freeserve.co.uk/richdawe/

*** /develop/djgpp/include/stdio.h	Fri Dec  8 22:46:26 2000
--- /develop/djgpp.dev/include/stdio.h	Sun May 20 18:57:42 2001
***************
*** 1,3 ****
--- 1,4 ----
+ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  
*************** void	rewind(FILE *_stream);
*** 97,102 ****
--- 98,104 ----
  int	scanf(const char *_format, ...);
  void	setbuf(FILE *_stream, char *_buf);
  int	setvbuf(FILE *_stream, char *_buf, int _mode, size_t _size);
+ int	snprintf(char *str, size_t n, const char *fmt, ...);
  int	sprintf(char *_s, const char *_format, ...);
  int	sscanf(const char *_s, const char *_format, ...);
  FILE *	tmpfile(void);
*************** char *	tmpnam(char *_s);
*** 104,109 ****
--- 106,112 ----
  int	ungetc(int _c, FILE *_stream);
  int	vfprintf(FILE *_stream, const char *_format, va_list _ap);
  int	vprintf(const char *_format, va_list _ap);
+ int	vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
  int	vsprintf(char *_s, const char *_format, va_list _ap);
  
  #ifndef __STRICT_ANSI__
*** /develop/djgpp/src/docs/kb/wc204.txi	Sat Apr 14 11:28:04 2001
--- /develop/djgpp.dev/src/docs/kb/wc204.txi	Sun May 20 18:57:42 2001
*************** would lose the exit status.
*** 375,377 ****
--- 375,381 ----
  File handles connected to the console device are no longer reported by
  the @code{select} function as not ready for input when termios functions
  are used to read those handles in cooked mode.
+ 
+ @findex snprintf
+ @findex vsnprintf
+ New functions @code{snprintf} and @code{vsnprintf} added.
*** /develop/djgpp/src/libc/ansi/stdio/flsbuf.c	Thu Jun  3 18:27:34 1999
--- /develop/djgpp.dev/src/libc/ansi/stdio/flsbuf.c	Sun May 20 18:57:42
2001
***************
*** 1,3 ****
--- 1,4 ----
+ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
*************** _flsbuf(int c, FILE *f)
*** 27,32 ****
--- 28,37 ----
    if ((f->_flag&_IOWRT)==0)
      return EOF;
  
+   /* No-op for full string buffers */
+   if (f->_flag & _IOSTRG)
+     return c;
+ 
    /* if the buffer is not yet allocated, allocate it */
    if ((base = f->_base) == NULL && (f->_flag & _IONBF) == 0)
    {
*** /develop/djgpp/src/libc/ansi/stdio/makefile	Sun Jun 28 18:44:16 1998
--- /develop/djgpp.dev/src/libc/ansi/stdio/makefile	Sun May 20 18:57:42
2001
***************
*** 1,3 ****
--- 1,4 ----
+ # Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details
  # Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details
  # Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
  TOP=../..
*************** SRC += ungetc.c
*** 61,65 ****
--- 62,68 ----
  SRC += vfprintf.c
  SRC += vprintf.c
  SRC += vsprintf.c
+ SRC += snprintf.c
+ SRC += vsnprntf.c
  
  include $(TOP)/../makefile.inc
*** /develop/djgpp/src/libc/ansi/stdio/snprintf.c	Sun Nov 12 21:57:18 2000
--- /develop/djgpp.dev/src/libc/ansi/stdio/snprintf.c	Sun May 20 18:57:42
2001
***************
*** 1 ****
--- 1,16 ----
+ /* Copyright (C) 2001 DJ Delorie see COPYING.DJ for details */
+ #include <stdarg.h>
+ #include <stdio.h>
  
+ int
+ snprintf(char *str, size_t n, const char *fmt, ...)
+ {
+   va_list ap;
+   int len;
+ 
+   va_start(ap, fmt);
+   len = vsnprintf(str, n, fmt, ap);
+   va_end(ap);
+ 
+   return len;
+ }
*** /develop/djgpp/src/libc/ansi/stdio/snprintf.txh	Sun Nov 12 21:57:18
2000
--- /develop/djgpp.dev/src/libc/ansi/stdio/snprintf.txh	Sun May 20
18:57:42 2001
***************
*** 1 ****
--- 1,32 ----
+ @node snprintf, stdio
+ @subheading Syntax
  
+ @example
+ #include <stdio.h>
+ 
+ int snprintf (char *@var{buffer}, size_t @var{n}, const char
*@var{format},
+               @dots{});
+ @end example
+ 
+ @subheading Description
+ 
+ This function works similarly to @code{sprintf()} (@pxref{sprintf}), but
+ the size @var{n} of the @var{buffer} is also taken into account.  This
+ function will write @var{n} - 1 characters.  The @var{n}th character is
used
+ for the terminating nul.  If @var{n} is zero, @var{buffer} is not
touched.
+ 
+ @subheading Return Value
+ 
+ The number of characters that would have been written (excluding the
trailing
+ nul) is returned; otherwise -1 is returned to flag encoding or buffer
space
+ errors.
+ 
+ The maximum accepted value of @var{n} is @code{INT_MAX}.  @code{INT_MAX}
is
+ defined in @code{<limits.h>}.  -1 is returned and @code{errno} is set to
+ @code{EFBIG}, if @var{n} is greater than this limit.
+ 
+ @subheading Portability
+ 
+ @port-note ansi The buffer size limit is imposed by DJGPP.  Other
systems may not have this limitation.
+ 
+ @portability ansi
*** /develop/djgpp/src/libc/ansi/stdio/sprintf.txh	Sun Sep 27 16:20:50
1998
--- /develop/djgpp.dev/src/libc/ansi/stdio/sprintf.txh	Sun May 20 18:57:42
2001
*************** int sprintf(char *buffer, const char *fo
*** 12,17 ****
--- 12,20 ----
  Sends formatted output from the arguments (@dots{}) to the @var{buffer}.
  @xref{printf}.
  
+ To avoid buffer overruns, it is safer to use @code{snprintf()}
+ (@pxref{snprintf}).
+ 
  @subheading Return Value
  
  The number of characters written.
*** /develop/djgpp/src/libc/ansi/stdio/vsnprntf.c	Sun Nov 12 21:57:18 2000
--- /develop/djgpp.dev/src/libc/ansi/stdio/vsnprntf.c	Sun May 20 19:58:28
2001
***************
*** 1 ****
--- 1,44 ----
+ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
+ #include <stdio.h>
+ #include <stdarg.h>
+ #include <limits.h>
+ #include <errno.h>
+ #include <libc/file.h>
  
+ int
+ vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
+ {
+   FILE _strbuf;
+   int len;
+ 
+   /* _cnt is an int in the FILE structure. To prevent wrap-around, we
limit
+    * n to between 0 and INT_MAX inclusively. */
+   if (n > INT_MAX)
+   {
+     errno = EFBIG;
+     return -1;
+   }
+ 
+   memset(&_strbuf, 0, sizeof(_strbuf));
+   _strbuf._flag = _IOWRT | _IOSTRG | _IONTERM;  
+ 
+   /* If n == 0, just querying how much space is needed. */
+   if (n > 0)
+   {
+     _strbuf._cnt = n - 1;
+     _strbuf._ptr = str;
+   }
+   else
+   {
+     _strbuf._cnt = 0;
+     _strbuf._ptr = NULL;
+   }
+ 
+   len = _doprnt(fmt, ap, &_strbuf);
+ 
+   /* Ensure nul termination */
+   if (n > 0)
+     *_strbuf._ptr = 0;
+ 
+   return len;
+ }
*** /develop/djgpp/src/libc/ansi/stdio/vsnprntf.txh	Sun Nov 12 21:57:18
2000
--- /develop/djgpp.dev/src/libc/ansi/stdio/vsnprntf.txh	Sun May 20
18:57:42 2001
***************
*** 1 ****
--- 1,33 ----
+ @node vsnprintf, stdio
+ @subheading Syntax
  
+ @example
+ #include <stdio.h>
+ #include <stdarg.h>
+ 
+ int vsnprintf (char *@var{buffer}, size_t @var{n}, const char
*@var{format},
+                va_list @var{ap});
+ @end example
+ 
+ @subheading Description
+ 
+ This function works similarly to @code{vsprintf()} (@pxref{vsprintf}),
but
+ the size @var{n} of the @var{buffer} is also taken into account.  This
+ function will write @var{n} - 1 characters.  The @var{n}th character is
used
+ for the terminating nul.  If @var{n} is zero, @var{buffer} is not
touched.
+ 
+ @subheading Return Value
+ 
+ The number of characters that would have been written (excluding the
trailing
+ nul) is returned; otherwise -1 is returned to flag encoding or buffer
space
+ errors.
+ 
+ The maximum accepted value of @var{n} is @code{INT_MAX}.  @code{INT_MAX}
is
+ defined in @code{<limits.h>}.  -1 is returned and @code{errno} is set to
+ @code{EFBIG}, if @var{n} is greater than this limit.
+ 
+ @subheading Portability
+ 
+ @port-note ansi The buffer size limit is imposed by DJGPP.  Other
systems may not have this limitation.
+ 
+ @portability ansi
*** /develop/djgpp/src/libc/ansi/stdio/vsprintf.txh	Sun Sep 27 16:20:50
1998
--- /develop/djgpp.dev/src/libc/ansi/stdio/vsprintf.txh	Sun May 20
18:57:42 2001
*************** int vsprintf(char *buffer, const char *f
*** 13,18 ****
--- 13,21 ----
  Sends formatted output from the @var{arguments} to the @var{buffer}.
  @xref{printf}. @xref{vfprintf}.
  
+ To avoid buffer overruns, it is safer to use @code{vsnprintf()}
+ (@pxref{vsnprintf}).
+ 
  @subheading Return Value
  
  The number of characters written.
*** /develop/djgpp/tests/libc/ansi/stdio/makefile	Sun Apr 18 16:14:36 1999
--- /develop/djgpp.dev/tests/libc/ansi/stdio/makefile	Sun May 20 18:57:42
2001
*************** SRC += printf.c
*** 16,20 ****
--- 16,22 ----
  SRC += sscanf.c
  SRC += tmpnam.c
  SRC += tscanf.c
+ SRC += tsnprtf.c
+ SRC += tsnprtf2.c
  
  include $(TOP)/../makefile.inc
*** /develop/djgpp/tests/libc/ansi/stdio/tsnprtf.c	Thu Jan  1 00:00:00
1970
--- /develop/djgpp.dev/tests/libc/ansi/stdio/tsnprtf.c	Sun May 20 20:11:10
2001
***************
*** 0 ****
--- 1,134 ----
+ /*
+  * tsnprtf.c - Test for snprintf()
+  */
+ 
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <limits.h>
+ 
+ int
+ main (void)
+ {
+   char BIG[] = "Hello this is a too big string for the buffer";
+   char holder[24];
+   int i, j;
+  
+   i = snprintf(holder, sizeof(holder), "%s\n", BIG);
+   printf("%s\n", BIG);
+   printf("%s\n", holder);
+   /*
+    * We are expecting :
+    *   i == strlen(BIG) + 1
+    * meaning the number that would have been written if the buffer was
+    * large enough (see C9X).
+    */
+   if (i != strlen(BIG) + 1 /* nul */)
+     {
+       fprintf(stderr, "FAILED snprintf\n");
+       fprintf(stderr,
+ 	      "sizeof (%ld), snprintf(%d), strlen(%ld)\n",
+ 	      sizeof(holder), i, strlen(BIG)) ;
+       exit(EXIT_FAILURE);
+     }
+ 
+   /*
+    * We may have broken sscanf since it is also a string stream
+    * Lets do a basic test.
+    */
+   {
+     static char line[] = "25 December 2000\n";
+     int day, year;
+     char month[24];
+ 
+     /* we are expecting to read 3 variables */
+     if ((i = sscanf(line, "%d %s %d\n", &day, month, &year)) == 3)
+       {
+ 	i = snprintf(holder, sizeof(holder), "%d %s %d\n", day, month, year);
+ 	printf(line);
+ 	j = printf(holder);
+ 	if (i != j)
+ 	  {
+ 	    fprintf(stderr, "FAILED snprintf\n");
+ 	    fprintf(stderr, "snprintf (%d) != printf (%d)\n", i, j);
+ 	    exit(EXIT_FAILURE);
+ 	  }
+       }
+     else
+       {
+ 	printf("sscanf (%d)\n", i);
+ 	printf("FAILED sscanf\n");
+ 	exit(EXIT_FAILURE);
+       }
+   }
+ 
+   /* Test length estimation - once with buffer, once without. */
+   *holder = '\0';
+   i = snprintf(holder, 0, "%s", BIG);
+   if ((i != strlen(BIG)) || (*holder != '\0') /* buffer touched */)
+     {
+       fprintf(stderr, "FAILED length estimation (with buffer)\n");
+       exit(EXIT_FAILURE);
+     }
+ 
+   i = snprintf(NULL, 0, "%s", BIG);
+   if (i != strlen(BIG))
+     {
+       fprintf(stderr, "FAILED length estimation (no buffer)\n");
+       exit(EXIT_FAILURE);
+     }
+ 
+   /* Try writing to a 1 byte buffer */
+   snprintf(holder, sizeof(holder), "%s", BIG);
+   i = snprintf(holder, 1, "%s", BIG);
+ 
+   if ((i < 0) || (*holder != '\0'))
+     {
+       fprintf(stderr, "FAILED termination only\n");
+       exit(EXIT_FAILURE);
+     }
+ 
+   /* Test maximum buffer size */
+   i = snprintf(holder, ((size_t) INT_MAX) + 1, "%s", BIG);
+ 
+   if (i >= 0)
+     {
+       fprintf(stderr, "FAILED too large buffer\n");
+       exit(EXIT_FAILURE);
+     }
+ 
+   /* Test padding a field to larger than buffer size. */
+   {
+     int s = sizeof(holder) * 16;
+ 
+     i = snprintf(holder, sizeof(holder), "%*s", s, BIG);
+ 
+     if ((i != s) || ((strlen(holder) + 1) != sizeof(holder)))
+       {
+ 	fprintf(stderr,
+ 		"FAILED with padding larger than buffer: %d output, "
+ 		"%ld written to buffer\n",
+ 		i, strlen(holder));
+ 	exit(EXIT_FAILURE);
+       }
+   }
+ 
+   /* Test precision to larger than buffer size. */
+   {
+     int s = sizeof(holder) * 4;
+ 
+     i = snprintf(holder, sizeof(holder), "%*.*e", s, s, 1e0);
+ 
+     if ((i <= s) || ((strlen(holder) + 1) != sizeof(holder)))
+       {
+ 	fprintf(stderr,
+ 		"FAILED with precision larger than buffer: %d output, "
+ 		"%ld written to buffer\n",
+ 		i, strlen(holder));
+ 	exit(EXIT_FAILURE);
+       }
+   }
+ 
+   /* signal success */
+   printf("SUCCESS\n");
+   return(EXIT_SUCCESS);
+ }
*** /develop/djgpp/tests/libc/ansi/stdio/tsnprtf2.c	Thu Jan  1 00:00:00
1970
--- /develop/djgpp.dev/tests/libc/ansi/stdio/tsnprtf2.c	Sun May 20
20:09:02 2001
***************
*** 0 ****
--- 1,35 ----
+ /*
+  * tsnprtf2.c - Test _doprnt() failure case for snprintf()
+  */
+ 
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <conio.h>
+ 
+ /* Simulate an encoding error by making _doprnt() fail every time.
snprintf()
+  * and vsnprintf() both invoke _doprnt(). */
+ 
+ int
+ _doprnt (const char *format, void *params, FILE *file)
+ {
+   return(-1);
+ }
+ 
+ int
+ main (void)
+ {
+   char holder[24];
+   int i;
+ 
+   /* Test handling of encoding errors */
+   i = snprintf(holder, sizeof(holder), "%s", "foo");
+ 
+   if (i >= 0)
+     {
+       fputs("FAILED generating encoding error", stderr);
+       exit(EXIT_FAILURE);
+     }
+ 
+   puts("SUCCESS");
+   return(EXIT_SUCCESS);
+ }

- Raw text -


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