diff --git a/ChangeLog b/ChangeLog index 981e0471796..0f045301ead 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1752,3 +1752,10 @@ information about RGMP. * lib/stdio/lib_fclose.c: Must flush all buffered data when the file is closed. Instead, it was discarding the buffered data. + * lib/stdio: All output stream logic was modified to support CONFIG_STDIO_LINEBUFFER. + If standard C buffered I/O is enabled (CONFIG_STDIO_BUFFER_SIZE > 0), then this + option may be added to force automatic, line-oriented flushing the output buffer + for printf() fprintf(), and vfprintf(). When a newline is encountered in the + format string, the output buffer will be flushed. This (slightly) increases + the NuttX footprint but supports the kind of behavior that people expect for + printf. diff --git a/Documentation/NuttxPortingGuide.html b/Documentation/NuttxPortingGuide.html index 3771d370d11..1ff52940b1e 100644 --- a/Documentation/NuttxPortingGuide.html +++ b/Documentation/NuttxPortingGuide.html @@ -3413,6 +3413,13 @@ build CONFIG_STDIO_BUFFER_SIZE: Size of the buffer to allocate on fopen. (Only if CONFIG_NFILE_STREAMS > 0) +
  • + CONFIG_STDIO_LINEBUFFER: + If standard C buffered I/O is enabled (CONFIG_STDIO_BUFFER_SIZE > 0), + then this option may be added to force automatic, line-oriented flushing the output buffer + for printf() >, fprintf() >, and vfprintf() >. + When a newline character is encountered in the format string, the output buffer will be flushed. + This (slightly) increases the NuttX footprint but supports the kind of behavior that people expect for printf.
  • CONFIG_NUNGET_CHARS: Number of characters that can be buffered by ungetc() (Only if CONFIG_NFILE_STREAMS > 0) diff --git a/configs/README.txt b/configs/README.txt index 88b36890790..50208f210cd 100644 --- a/configs/README.txt +++ b/configs/README.txt @@ -503,6 +503,13 @@ defconfig -- This is a configuration file similar to the Linux CONFIG_NAME_MAX - The maximum size of a file name. CONFIG_STDIO_BUFFER_SIZE - Size of the buffer to allocate on fopen. (Only if CONFIG_NFILE_STREAMS > 0) + CONFIG_STDIO_LINEBUFFER - If standard C buffered I/O is enabled + (CONFIG_STDIO_BUFFER_SIZE > 0), then this option may be added + to force automatic, line-oriented flushing the output buffer + for printf() fprintf(), and vfprintf(). When a newline is + encountered in the format string, the output buffer will be + flushed. This (slightly) increases the NuttX footprint but + supports the kind of behavior that people expect for printf. CONFIG_NUNGET_CHARS - Number of characters that can be buffered by ungetc() (Only if CONFIG_NFILE_STREAMS > 0) CONFIG_PREALLOC_MQ_MSGS - The number of pre-allocated message diff --git a/include/nuttx/streams.h b/include/nuttx/streams.h index cdba45e9e20..e8b6e2b2dfa 100644 --- a/include/nuttx/streams.h +++ b/include/nuttx/streams.h @@ -1,7 +1,7 @@ /**************************************************************************** * include/nuttx/streams.h * - * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -56,20 +56,24 @@ struct lib_instream_s; struct lib_outstream_s; -typedef int (*lib_getc_t)(FAR struct lib_instream_s *this); +typedef int (*lib_getc_t)(FAR struct lib_instream_s *this); typedef void (*lib_putc_t)(FAR struct lib_outstream_s *this, int ch); +typedef int (*lib_flush_t)(FAR struct lib_outstream_s *this); struct lib_instream_s { - lib_getc_t get; /* Pointer to function to get one character */ - int nget; /* Total number of characters gotten. Written + lib_getc_t get; /* Pointer to function to get one character */ + int nget; /* Total number of characters gotten. Written * by get method, readable by user */ }; struct lib_outstream_s { - lib_putc_t put; /* Pointer to function to put one character */ - int nput; /* Total number of characters put. Written + lib_putc_t put; /* Pointer to function to put one character */ +#ifdef CONFIG_STDIO_LINEBUFFER + lib_flush_t flush; /* Pointer to function flush buffered characters */ +#endif + int nput; /* Total number of characters put. Written * by put method, readable by user */ }; diff --git a/lib/lib_internal.h b/lib/lib_internal.h index b9c74304e62..6c9b9c14c7d 100644 --- a/lib/lib_internal.h +++ b/lib/lib_internal.h @@ -110,10 +110,16 @@ extern void stream_semtake(FAR struct streamlist *list); extern void stream_semgive(FAR struct streamlist *list); #endif +/* Defined in lib_libnoflush.c */ + +#ifdef CONFIG_STDIO_LINEBUFFER +extern int lib_noflush(FAR struct lib_outstream_s *this); +#endif + /* Defined in lib_libsprintf.c */ -extern int lib_sprintf (FAR struct lib_outstream_s *obj, - const char *fmt, ...); +extern int lib_sprintf(FAR struct lib_outstream_s *obj, + const char *fmt, ...); /* Defined lib_libvsprintf.c */ diff --git a/lib/stdio/Make.defs b/lib/stdio/Make.defs index 9478369f3ce..fe6e106bb6b 100644 --- a/lib/stdio/Make.defs +++ b/lib/stdio/Make.defs @@ -51,6 +51,11 @@ STDIO_SRCS += lib_fopen.c lib_fclose.c lib_fread.c lib_libfread.c lib_fseek.c \ lib_fprintf.c lib_vfprintf.c lib_stdinstream.c lib_stdoutstream.c endif endif + ifeq ($(CONFIG_LIBC_FLOATINGPOINT),y) STDIO_SRCS += lib_dtoa.c endif + +ifeq ($(CONFIG_STDIO_LINEBUFFER),y) +STDIO_SRCS += lib_libnoflush.c +endif diff --git a/lib/stdio/lib_libnoflush.c b/lib/stdio/lib_libnoflush.c new file mode 100644 index 00000000000..90e5f9f5cf4 --- /dev/null +++ b/lib/stdio/lib_libnoflush.c @@ -0,0 +1,103 @@ +/**************************************************************************** + * lib/stdio/lib_libnoflush.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "lib_internal.h" + +#ifdef CONFIG_STDIO_LINEBUFFER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Global Constant Data + ****************************************************************************/ + +/**************************************************************************** + * Global Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Constant Data + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Global Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_noflush + * + * Description: + * lib_noflush() provides a common, dummy flush method for output streams + * that are not flushable. Only used if CONFIG_STDIO_LINEBUFFER is selected. + * + * Return: + * Always returns OK + * + ****************************************************************************/ + +int lib_noflush(FAR struct lib_outstream_s *this) +{ + return OK; +} + +#endif /* CONFIG_STDIO_LINEBUFFER */ + diff --git a/lib/stdio/lib_libvsprintf.c b/lib/stdio/lib_libvsprintf.c index 24591d87555..8e0709ec90a 100644 --- a/lib/stdio/lib_libvsprintf.c +++ b/lib/stdio/lib_libvsprintf.c @@ -1127,7 +1127,20 @@ int lib_vsprintf(FAR struct lib_outstream_s *obj, const char *src, va_list ap) if (*src != '%') { + /* Output the character */ + obj->put(obj, *src); + + /* Flush the buffer if a newline is encountered */ + +#ifdef CONFIG_STDIO_LINEBUFFER + if (*src == '\n') + { + (void)obj->flush(obj); + } +#endif + /* Process the next character in the format */ + continue; } diff --git a/lib/stdio/lib_lowinstream.c b/lib/stdio/lib_lowinstream.c index 85f916acb93..6dcc4a37e67 100644 --- a/lib/stdio/lib_lowinstream.c +++ b/lib/stdio/lib_lowinstream.c @@ -55,7 +55,7 @@ * Name: lowinstream_getc ****************************************************************************/ -static int lowinstream_getc(FAR struct lib_outstream_s *this) +static int lowinstream_getc(FAR struct lib_instream_s *this) { if (this && up_getc(ch) != EOF) { @@ -82,7 +82,7 @@ static int lowinstream_getc(FAR struct lib_outstream_s *this) * ****************************************************************************/ -void lib_lowinstream(FAR struct lib_outstream_s *stream) +void lib_lowinstream(FAR struct lib_instream_s *stream) { stream->get = lowinstream_getc; stream->nget = 0; diff --git a/lib/stdio/lib_lowoutstream.c b/lib/stdio/lib_lowoutstream.c index 2ab7c9f675b..3b3d467b280 100644 --- a/lib/stdio/lib_lowoutstream.c +++ b/lib/stdio/lib_lowoutstream.c @@ -84,8 +84,11 @@ static void lowoutstream_putc(FAR struct lib_outstream_s *this, int ch) void lib_lowoutstream(FAR struct lib_outstream_s *stream) { - stream->put = lowoutstream_putc; - stream->nput = 0; + stream->put = lowoutstream_putc; +#ifdef CONFIG_STDIO_LINEBUFFER + stream->flush = lib_noflush; +#endif + stream->nput = 0; } #endif /* CONFIG_ARCH_LOWPUTC */ diff --git a/lib/stdio/lib_memoutstream.c b/lib/stdio/lib_memoutstream.c index 1cf2a8f29ad..d5a673b3ae7 100644 --- a/lib/stdio/lib_memoutstream.c +++ b/lib/stdio/lib_memoutstream.c @@ -82,10 +82,13 @@ static void memoutstream_putc(FAR struct lib_outstream_s *this, int ch) void lib_memoutstream(FAR struct lib_memoutstream_s *memoutstream, FAR char *bufstart, int buflen) { - memoutstream->public.put = memoutstream_putc; - memoutstream->public.nput = 0; /* Will be buffer index */ - memoutstream->buffer = bufstart; /* Start of buffer */ - memoutstream->buflen = buflen - 1; /* Save space for null terminator */ + memoutstream->public.put = memoutstream_putc; +#ifdef CONFIG_STDIO_LINEBUFFER + memoutstream->public.flush = lib_noflush; +#endif + memoutstream->public.nput = 0; /* Will be buffer index */ + memoutstream->buffer = bufstart; /* Start of buffer */ + memoutstream->buflen = buflen - 1; /* Save space for null terminator */ } diff --git a/lib/stdio/lib_nulloutstream.c b/lib/stdio/lib_nulloutstream.c index 85b7daa9e27..f92cb0f3364 100644 --- a/lib/stdio/lib_nulloutstream.c +++ b/lib/stdio/lib_nulloutstream.c @@ -72,7 +72,10 @@ static void nulloutstream_putc(FAR struct lib_outstream_s *this, int ch) void lib_nulloutstream(FAR struct lib_outstream_s *nulloutstream) { - nulloutstream->put = nulloutstream_putc; - nulloutstream->nput = 0; + nulloutstream->put = nulloutstream_putc; +#ifdef CONFIG_STDIO_LINEBUFFER + nulloutstream->flush = lib_noflush; +#endif + nulloutstream->nput = 0; } diff --git a/lib/stdio/lib_rawoutstream.c b/lib/stdio/lib_rawoutstream.c index 05cb5853ff2..bea47f3ce6b 100644 --- a/lib/stdio/lib_rawoutstream.c +++ b/lib/stdio/lib_rawoutstream.c @@ -91,8 +91,11 @@ static void rawoutstream_putc(FAR struct lib_outstream_s *this, int ch) void lib_rawoutstream(FAR struct lib_rawoutstream_s *rawoutstream, int fd) { - rawoutstream->public.put = rawoutstream_putc; - rawoutstream->public.nput = 0; - rawoutstream->fd = fd; + rawoutstream->public.put = rawoutstream_putc; +#ifdef CONFIG_STDIO_LINEBUFFER + rawoutstream->public.flush = lib_noflush; +#endif + rawoutstream->public.nput = 0; + rawoutstream->fd = fd; } diff --git a/lib/stdio/lib_stdoutstream.c b/lib/stdio/lib_stdoutstream.c index 272a93309a2..99fae11b29e 100644 --- a/lib/stdio/lib_stdoutstream.c +++ b/lib/stdio/lib_stdoutstream.c @@ -59,6 +59,18 @@ static void stdoutstream_putc(FAR struct lib_outstream_s *this, int ch) } } +/**************************************************************************** + * Name: stdoutstream_flush + ****************************************************************************/ + +#if defined(CONFIG_STDIO_LINEBUFFER) && CONFIG_STDIO_BUFFER_SIZE > 0 +int stdoutstream_flush(FAR struct lib_outstream_s *this) +{ + FAR struct lib_stdoutstream_s *sthis = (FAR struct lib_stdoutstream_s *)this; + return lib_fflush(sthis->stream, true); +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -83,9 +95,16 @@ static void stdoutstream_putc(FAR struct lib_outstream_s *this, int ch) void lib_stdoutstream(FAR struct lib_stdoutstream_s *stdoutstream, FAR FILE *stream) { - stdoutstream->public.put = stdoutstream_putc; - stdoutstream->public.nput = 0; - stdoutstream->stream = stream; + stdoutstream->public.put = stdoutstream_putc; +#ifdef CONFIG_STDIO_LINEBUFFER +#if CONFIG_STDIO_BUFFER_SIZE > 0 + stdoutstream->public.flush = stdoutstream_flush; +#else + stdoutstream->public.flush = lib_noflush; +#endif +#endif + stdoutstream->public.nput = 0; + stdoutstream->stream = stream; } diff --git a/tools/mkconfig.c b/tools/mkconfig.c index c8a6fb261ed..13598938c9b 100644 --- a/tools/mkconfig.c +++ b/tools/mkconfig.c @@ -184,6 +184,13 @@ int main(int argc, char **argv, char **envp) printf("# undef CONFIG_NUNGET_CHARS\n"); printf("# define CONFIG_NUNGET_CHARS 0\n"); printf("#endif\n\n"); + printf("/* If no standard C buffered I/O is not supported, then line-oriented buffering\n"); + printf(" * cannot be supported.\n"); + printf(" */\n\n"); + printf("#if CONFIG_STDIO_BUFFER_SIZE == 0\n"); + printf("# undef CONFIG_STDIO_LINEBUFFER\n"); + printf("# define CONFIG_STDIO_LINEBUFFER 0\n"); + printf("#endif\n\n"); printf("/* If the maximum message size is zero, then we assume that message queues\n"); printf(" * support should be disabled\n"); printf(" */\n\n");