diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-01-08 10:47:34 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-01-08 10:47:34 -0600 |
commit | eaaec3ec0947c82d1d94039fb25c57fa33134b87 (patch) | |
tree | f935b9c7943b8026b8553debd37e32685ea9a9d6 | |
parent | 5f5b6293926e29c6c2101f8921c808333260b9b9 (diff) | |
download | px4-nuttx-eaaec3ec0947c82d1d94039fb25c57fa33134b87.tar.gz px4-nuttx-eaaec3ec0947c82d1d94039fb25c57fa33134b87.tar.bz2 px4-nuttx-eaaec3ec0947c82d1d94039fb25c57fa33134b87.zip |
Add lib_libfgets() to better support the small differences between gets(), gets_s(), and fgets()
-rw-r--r-- | nuttx/ChangeLog | 3 | ||||
-rw-r--r-- | nuttx/libc/lib_internal.h | 7 | ||||
-rw-r--r-- | nuttx/libc/stdio/Make.defs | 11 | ||||
-rw-r--r-- | nuttx/libc/stdio/lib_fgets.c | 143 | ||||
-rw-r--r-- | nuttx/libc/stdio/lib_gets.c | 57 | ||||
-rw-r--r-- | nuttx/libc/stdio/lib_gets_s.c | 66 | ||||
-rw-r--r-- | nuttx/libc/stdio/lib_libfgets.c | 252 |
7 files changed, 297 insertions, 242 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index cfe4e52c2..ef93b992c 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -6390,4 +6390,7 @@ ("x") (2014-1-7) * include/threads.h: First crude cut at a C11 threads.h header file (just maps to pthreads) (2014-1-7). + * libc/stdio/lib_libfgets.c: Common implementation supports all + of the slightly different requirements of gets(), gets_s(), and + fgets() (2014-1-7). diff --git a/nuttx/libc/lib_internal.h b/nuttx/libc/lib_internal.h index ae6ec7b06..bf73b72a4 100644 --- a/nuttx/libc/lib_internal.h +++ b/nuttx/libc/lib_internal.h @@ -1,7 +1,7 @@ /**************************************************************************** * libc/lib_internal.h * - * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -184,6 +184,11 @@ ssize_t lib_fwrite(FAR const void *ptr, size_t count, FAR FILE *stream); ssize_t lib_fread(FAR void *ptr, size_t count, FAR FILE *stream); +/* Defined in lib_libfgets.c */ + +FAR char *lib_fgets(FAR char *buf, int buflen, FILE *stream, + bool keepnl, bool consume); + /* Defined in lib_libfflush.c */ ssize_t lib_fflush(FAR FILE *stream, bool bforce); diff --git a/nuttx/libc/stdio/Make.defs b/nuttx/libc/stdio/Make.defs index 2a6ea8563..c0a523091 100644 --- a/nuttx/libc/stdio/Make.defs +++ b/nuttx/libc/stdio/Make.defs @@ -57,11 +57,12 @@ ifneq ($(CONFIG_NFILE_STREAMS),0) CSRCS += lib_fopen.c lib_fclose.c lib_fread.c lib_libfread.c lib_fseek.c CSRCS += lib_ftell.c lib_fsetpos.c lib_fgetpos.c lib_fgetc.c lib_fgets.c -CSRCS += lib_gets_s.c lib_gets.c lib_fwrite.c lib_libfwrite.c lib_fflush.c -CSRCS += lib_libflushall.c lib_libfflush.c lib_rdflush.c lib_wrflush.c -CSRCS += lib_fputc.c lib_puts.c lib_fputs.c lib_ungetc.c lib_vprintf.c -CSRCS += lib_fprintf.c lib_vfprintf.c lib_stdinstream.c lib_stdoutstream.c -CSRCS += lib_perror.c lib_feof.c lib_ferror.c lib_clearerr.c +CSRCS += lib_gets_s.c lib_gets.c lib_libfgets.c lib_fwrite.c lib_libfwrite.c +CSRCS += lib_fflush.c lib_libflushall.c lib_libfflush.c lib_rdflush.c +CSRCS += lib_wrflush.c lib_fputc.c lib_puts.c lib_fputs.c lib_ungetc.c +CSRCS += lib_vprintf.c lib_fprintf.c lib_vfprintf.c lib_stdinstream.c +CSRCS += lib_stdoutstream.c lib_perror.c lib_feof.c lib_ferror.c +CSRCS += lib_clearerr.c endif endif diff --git a/nuttx/libc/stdio/lib_fgets.c b/nuttx/libc/stdio/lib_fgets.c index 198e44be5..3aff54db9 100644 --- a/nuttx/libc/stdio/lib_fgets.c +++ b/nuttx/libc/stdio/lib_fgets.c @@ -1,7 +1,7 @@ /**************************************************************************** * libc/stdio/lib_fgets.c * - * Copyright (C) 2007-2008, 2011-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2008, 2011-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -39,58 +39,12 @@ #include <nuttx/config.h> -#include <stdbool.h> #include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <ctype.h> -#include <debug.h> -/**************************************************************************** - * Definitions - ****************************************************************************/ -/* Some environments may return CR as end-of-line, others LF, and others - * both. If not specified, the logic here assumes either (but not both) as - * the default. - */ - -#if defined(CONFIG_EOL_IS_CR) -# undef CONFIG_EOL_IS_LF -# undef CONFIG_EOL_IS_BOTH_CRLF -# undef CONFIG_EOL_IS_EITHER_CRLF -#elif defined(CONFIG_EOL_IS_LF) -# undef CONFIG_EOL_IS_CR -# undef CONFIG_EOL_IS_BOTH_CRLF -# undef CONFIG_EOL_IS_EITHER_CRLF -#elif defined(CONFIG_EOL_IS_BOTH_CRLF) -# undef CONFIG_EOL_IS_CR -# undef CONFIG_EOL_IS_LF -# undef CONFIG_EOL_IS_EITHER_CRLF -#elif defined(CONFIG_EOL_IS_EITHER_CRLF) -# undef CONFIG_EOL_IS_CR -# undef CONFIG_EOL_IS_LF -# undef CONFIG_EOL_IS_BOTH_CRLF -#else -# undef CONFIG_EOL_IS_CR -# undef CONFIG_EOL_IS_LF -# undef CONFIG_EOL_IS_BOTH_CRLF -# define CONFIG_EOL_IS_EITHER_CRLF 1 -#endif - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ +#include "lib_internal.h" /**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Data + * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** @@ -98,7 +52,7 @@ ****************************************************************************/ /**************************************************************************** - * Global Functions + * Public Functions ****************************************************************************/ /**************************************************************************** @@ -115,92 +69,7 @@ char *fgets(FAR char *buf, int buflen, FILE *stream) { - int nch = 0; - - /* Sanity checks */ - - if (!stream || !buf || buflen < 1 || stream->fs_fd < 0) - { - return NULL; - } - - if (buflen < 2) - { - *buf = '\0'; - return buf; - } - - /* Read characters until we have a full line. On each the loop we must - * be assured that there are two free bytes in the line buffer: One for - * the next character and one for the null terminator. - */ - - for(;;) - { - /* Get the next character */ - - int ch = fgetc(stream); - - /* Check for end-of-line. This is tricky only in that some - * environments may return CR as end-of-line, others LF, and - * others both. - */ - -#if defined(CONFIG_EOL_IS_LF) || defined(CONFIG_EOL_IS_BOTH_CRLF) - if (ch == '\n') -#elif defined(CONFIG_EOL_IS_CR) - if (ch == '\r') -#else /* elif defined(CONFIG_EOL_IS_EITHER_CRLF) */ - if (ch == '\n' || ch == '\r') -#endif - { - /* The newline is stored in the buffer along with the null - * terminator. - */ - - buf[nch++] = '\n'; - buf[nch] = '\0'; - return buf; - } - - /* Check for end-of-file */ - - else if (ch == EOF) - { - /* End of file with no data? */ - - if (!nch) - { - /* Yes.. return NULL as the end of file mark */ - - return NULL; - } - else - { - /* Terminate the line */ - - buf[nch] = '\0'; - return buf; - } - } - - /* Otherwise, check if the character is printable and, if so, put the - * character in the line buffer - */ - - else if (isprint(ch)) - { - buf[nch++] = ch; - - /* Check if there is room for another character and the line's - * null terminator. If not then we have to end the line now. - */ + /* Let lib_fgets() do the heavy lifting */ - if (nch + 1 >= buflen) - { - buf[nch] = '\0'; - return buf; - } - } - } + return lib_fgets(buf, buflen, stream, true, false); } diff --git a/nuttx/libc/stdio/lib_gets.c b/nuttx/libc/stdio/lib_gets.c index 39c31d273..e9e36987b 100644 --- a/nuttx/libc/stdio/lib_gets.c +++ b/nuttx/libc/stdio/lib_gets.c @@ -37,40 +37,15 @@ * Included Files ****************************************************************************/ -#include <stdio.h> -#include <limits.h> -#include <string.h> - -/**************************************************************************** - * Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Global Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Global Constant Data - ****************************************************************************/ +#include <nuttx/config.h> -/**************************************************************************** - * Global Variables - ****************************************************************************/ +#include <stdint.h> +#include <stdio.h> -/**************************************************************************** - * Private Constant Data - ****************************************************************************/ +#include "lib_internal.h" /**************************************************************************** - * Private Variables + * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** @@ -96,25 +71,7 @@ FAR char *gets(FAR char *s) { - /* gets is ALMOST the same as fgets using stdin and no length limit - * (hence, the unsafeness of gets). So let fgets do most of the work. - */ + /* Let lib_fgets() do the heavy lifting */ - FAR char *ret = fgets(s, INT_MAX, stdin); - if (ret) - { - /* Another subtle difference from fgets is that gets replaces - * end-of-line markers with null terminators. We will do that as - * a second step (with some loss in performance). - */ - - int len = strlen(ret); - if (len > 0 && ret[len-1] == '\n') - { - ret[len-1] = '\0'; - } - } - - return ret; + return lib_fgets(s, RSIZE_MAX, stdin, false, false); } - diff --git a/nuttx/libc/stdio/lib_gets_s.c b/nuttx/libc/stdio/lib_gets_s.c index 95816b080..3f9ecc0cb 100644 --- a/nuttx/libc/stdio/lib_gets_s.c +++ b/nuttx/libc/stdio/lib_gets_s.c @@ -37,40 +37,15 @@ * Included Files ****************************************************************************/ -#include <stdio.h> -#include <limits.h> -#include <string.h> - -/**************************************************************************** - * Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Global Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Global Constant Data - ****************************************************************************/ +#include <nuttx/config.h> -/**************************************************************************** - * Global Variables - ****************************************************************************/ +#include <stdio.h> +#include <stdint.h> -/**************************************************************************** - * Private Constant Data - ****************************************************************************/ +#include "lib_internal.h" /**************************************************************************** - * Private Variables + * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** @@ -88,9 +63,10 @@ * gets() reads a line from stdin into the buffer pointed to by s until * either a terminating newline or EOF, which it replaces with '\0'. Reads * at most n-1 characters from stdin into the array pointed to by str until - * new-line character, end-of-file condition, or read error. A null - * character is written immediately after the last character read into the - * array, or to str[0] if no characters were read. + * new-line character, end-of-file condition, or read error. The newline + * character, if encountered, is not saved in the arraay. A NUL character + * is written immediately after the last character read into the array, or + * to str[0] if no characters were read. * * If n is zero or is greater than RSIZE_MAX, a null character is written * to str[0] but the function reads and discards characters from stdin @@ -99,30 +75,22 @@ * * If n-1 characters have been read, continues reading and discarding the * characters from stdin until new-line character, end-of-file condition, - * or read error (not implemented). + * or read error. * **************************************************************************/ FAR char *gets_s(FAR char *s, rsize_t n) { - /* gets is equivalent to fgets using stdin. So let fgets do most of the - * work. - */ + /* Handle the case where n is out of range as required */ - FAR char *ret = fgets(s, n, stdin); - if (ret) + if (n < 1 || n > RSIZE_MAX) { - /* A subtle difference from fgets is that gets_s replaces end-of-line - * markers with null terminators. We will do that as a second step - * (with some loss in performance). - */ + /* Set n=1, i.e., room only for the NUL terminator */ - int len = strlen(ret); - if (len > 0 && ret[len-1] == '\n') - { - ret[len-1] = '\0'; - } + n = 1; } - return ret; + /* Then let lib_fgets() do the heavy lifting */ + + return lib_fgets(s, n, stdin, false, true); } diff --git a/nuttx/libc/stdio/lib_libfgets.c b/nuttx/libc/stdio/lib_libfgets.c new file mode 100644 index 000000000..6a8cdeb44 --- /dev/null +++ b/nuttx/libc/stdio/lib_libfgets.c @@ -0,0 +1,252 @@ +/**************************************************************************** + * libc/stdio/lib_libfgets.c + * + * Copyright (C) 2007-2008, 2011-2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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 <nuttx/config.h> + +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <debug.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Some environments may return CR as end-of-line, others LF, and others + * both. If not specified, the logic here assumes either (but not both) as + * the default. + */ + +#if defined(CONFIG_EOL_IS_CR) +# undef CONFIG_EOL_IS_LF +# undef CONFIG_EOL_IS_BOTH_CRLF +# undef CONFIG_EOL_IS_EITHER_CRLF +#elif defined(CONFIG_EOL_IS_LF) +# undef CONFIG_EOL_IS_CR +# undef CONFIG_EOL_IS_BOTH_CRLF +# undef CONFIG_EOL_IS_EITHER_CRLF +#elif defined(CONFIG_EOL_IS_BOTH_CRLF) +# undef CONFIG_EOL_IS_CR +# undef CONFIG_EOL_IS_LF +# undef CONFIG_EOL_IS_EITHER_CRLF +#elif defined(CONFIG_EOL_IS_EITHER_CRLF) +# undef CONFIG_EOL_IS_CR +# undef CONFIG_EOL_IS_LF +# undef CONFIG_EOL_IS_BOTH_CRLF +#else +# undef CONFIG_EOL_IS_CR +# undef CONFIG_EOL_IS_LF +# undef CONFIG_EOL_IS_BOTH_CRLF +# define CONFIG_EOL_IS_EITHER_CRLF 1 +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: consume_eol + * + * Description: + * If 'consume' is true, then consume_eol() will read and discard bytes from + * 'stream' until an EOF or a newline encountered or until a read error + * occurs. + * + **************************************************************************/ + +static void consume_eol(FILE *stream, bool consume) +{ + if (consume) + { + int ch; + + do + { + ch = fgetc(stream); + } +#if defined(CONFIG_EOL_IS_LF) || defined(CONFIG_EOL_IS_BOTH_CRLF) + while (ch != EOF && ch != '\n'); +#elif defined(CONFIG_EOL_IS_CR) + while (ch != EOF && ch != '\r'); +#else /* elif defined(CONFIG_EOL_IS_EITHER_CRLF) */ + while (ch != EOF && ch != '\n' && ch != '\r'); +#endif + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_fgets + * + * Description: + * lib_fgets() implements the core logic for both fgets() and gets_s(). + * lib_fgets() reads in at most one less than 'buflen' characters from + * stream and stores them into the buffer pointed to by 'buf'. Reading + * stops after an EOF or a newline encountered or after a read error + * occurs. + * + * If a newline is read, it is stored into the buffer only if 'keepnl' is + * set true. A null terminator is always stored after the last character + * in the buffer. + * + * If 'buflen'-1 bytes were read into 'buf' without encountering an EOF + * or newline then the following behavior depends on the value of + * 'consume': If consume is true, then lib_fgets() will continue reading + * bytes and discarding them until an EOF or a newline encountered or + * until a read error occurs. Otherwise, lib_fgets() returns with the + * remaining of the incoming stream buffer. + * + **************************************************************************/ + +FAR char *lib_fgets(FAR char *buf, int buflen, FILE *stream, + bool keepnl, bool consume) +{ + int nch = 0; + + /* Sanity checks */ + + if (!stream || !buf || stream->fs_fd < 0) + { + return NULL; + } + + /* Make sure that we have a buffer and space for at least one character */ + + if (buflen < 1) + { + consume_eol(stream, consume); + return NULL; + } + + /* Make sure that we have space for something in addition to the NUL + * terminator. + */ + + if (buflen < 2) + { + *buf = '\0'; + consume_eol(stream, consume); + return buf; + } + + /* Read characters until we have a full line. On each the loop we must + * be assured that there are two free bytes in the line buffer: One for + * the next character and one for the null terminator. + */ + + for(;;) + { + /* Get the next character */ + + int ch = fgetc(stream); + + /* Check for end-of-line. This is tricky only in that some + * environments may return CR as end-of-line, others LF, and + * others both. + */ + +#if defined(CONFIG_EOL_IS_LF) || defined(CONFIG_EOL_IS_BOTH_CRLF) + if (ch == '\n') +#elif defined(CONFIG_EOL_IS_CR) + if (ch == '\r') +#else /* elif defined(CONFIG_EOL_IS_EITHER_CRLF) */ + if (ch == '\n' || ch == '\r') +#endif + { + if (keepnl) + { + /* Store newline is stored in the buffer */ + + buf[nch++] = '\n'; + } + + /* Terminate the string */ + + buf[nch] = '\0'; + return buf; + } + + /* Check for end-of-file */ + + else if (ch == EOF) + { + /* End of file with no data? */ + + if (!nch) + { + /* Yes.. return NULL as the end of file mark */ + + return NULL; + } + else + { + /* No, terminate the accumulated string */ + + buf[nch] = '\0'; + return buf; + } + } + + /* Otherwise, check if the character is printable and, if so, put the + * character in the line buffer + */ + + else if (isprint(ch)) + { + buf[nch++] = ch; + + /* Check if there is room for another character and the line's + * NUL terminator. If not then we have to end the line now, + * perhaps consuming any data up to the end-of-line. + */ + + if (nch + 1 >= buflen) + { + buf[nch] = '\0'; + consume_eol(stream, consume); + return buf; + } + } + } +} |