From cd74a1df22c2d3aa90ae76db3073ec4a0b28a9e5 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Thu, 18 Nov 2021 03:42:07 +0800 Subject: [PATCH] libc/stdio: Make gets/gets_s work without CONFIG_FILE_STREAM Signed-off-by: Xiang Xiao --- libs/libc/libc.h | 5 + libs/libc/stdio/Make.defs | 3 +- libs/libc/stdio/lib_gets.c | 5 + libs/libc/stdio/lib_gets_s.c | 5 + libs/libc/stdio/lib_libdgets.c | 207 +++++++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 libs/libc/stdio/lib_libdgets.c diff --git a/libs/libc/libc.h b/libs/libc/libc.h index 97420528ae..7a81e44549 100644 --- a/libs/libc/libc.h +++ b/libs/libc/libc.h @@ -97,6 +97,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_libgets.c */ + +FAR char *lib_dgets(FAR char *buf, size_t buflen, int fd, + bool keepnl, bool consume); + /* Defined in lib_libfgets.c */ FAR char *lib_fgets(FAR char *buf, size_t buflen, FILE *stream, diff --git a/libs/libc/stdio/Make.defs b/libs/libc/stdio/Make.defs index 47aa9601ea..bc028cb372 100644 --- a/libs/libc/stdio/Make.defs +++ b/libs/libc/stdio/Make.defs @@ -25,6 +25,7 @@ CSRCS += lib_fileno.c lib_printf.c lib_sprintf.c lib_asprintf.c CSRCS += lib_snprintf.c lib_libsprintf.c lib_vsprintf.c lib_vasprintf.c CSRCS += lib_vsnprintf.c lib_dprintf.c lib_vdprintf.c lib_vprintf.c CSRCS += lib_perror.c lib_putchar.c lib_getchar.c lib_puts.c +CSRCS += lib_gets_s.c lib_gets.c lib_libdgets.c CSRCS += lib_sscanf.c lib_vsscanf.c lib_libvscanf.c lib_libvsprintf.c CSRCS += lib_remove.c lib_tempnam.c lib_tmpnam.c lib_ultoa_invert.c CSRCS += lib_renameat.c @@ -39,7 +40,7 @@ ifeq ($(CONFIG_FILE_STREAM),y) CSRCS += lib_fopen.c lib_freopen.c lib_fclose.c lib_fread.c lib_libfread.c CSRCS += lib_fseek.c lib_fseeko.c lib_ftell.c lib_ftello.c lib_fsetpos.c CSRCS += lib_getdelim.c lib_fgetpos.c lib_getc.c lib_fgetc.c -CSRCS += lib_fgets.c lib_gets_s.c lib_gets.c lib_libfgets.c lib_fwrite.c +CSRCS += lib_fgets.c lib_libfgets.c lib_fwrite.c CSRCS += lib_libfwrite.c lib_fflush.c lib_libflushall.c lib_libfflush.c CSRCS += lib_rdflush.c lib_wrflush.c lib_putc.c lib_fputc.c CSRCS += lib_fputs.c lib_ungetc.c lib_fprintf.c lib_vfprintf.c diff --git a/libs/libc/stdio/lib_gets.c b/libs/libc/stdio/lib_gets.c index 2a25e8fc8a..c39d4471a4 100644 --- a/libs/libc/stdio/lib_gets.c +++ b/libs/libc/stdio/lib_gets.c @@ -26,6 +26,7 @@ #include #include +#include #include "libc.h" @@ -58,5 +59,9 @@ FAR char *gets(FAR char *s) { /* Let lib_fgets() do the heavy lifting */ +#ifdef CONFIG_FILE_STREAM return lib_fgets(s, SIZE_MAX, stdin, false, false); +#else + return lib_dgets(s, SIZE_MAX, STDIN_FILENO, false, false); +#endif } diff --git a/libs/libc/stdio/lib_gets_s.c b/libs/libc/stdio/lib_gets_s.c index ddcaa8118c..fb9a26584f 100644 --- a/libs/libc/stdio/lib_gets_s.c +++ b/libs/libc/stdio/lib_gets_s.c @@ -26,6 +26,7 @@ #include #include +#include #include "libc.h" @@ -69,5 +70,9 @@ FAR char *gets_s(FAR char *s, rsize_t n) /* Then let lib_fgets() do the heavy lifting */ +#ifdef CONFIG_FILE_STREAM return lib_fgets(s, n, stdin, false, true); +#else + return lib_dgets(s, n, STDIN_FILENO, false, true); +#endif } diff --git a/libs/libc/stdio/lib_libdgets.c b/libs/libc/stdio/lib_libdgets.c new file mode 100644 index 0000000000..22815168a5 --- /dev/null +++ b/libs/libc/stdio/lib_libdgets.c @@ -0,0 +1,207 @@ +/**************************************************************************** + * libs/libc/stdio/lib_libdgets.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "libc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dgetc(int fd) +{ + unsigned char c; + return read(fd, &c, 1) == 1 ? c : EOF; +} + +/**************************************************************************** + * Name: consume_eol + * + * Description: + * If 'consume' is true, then consume_eol() will read and discard bytes + * from 'fd' until an EOF or a newline encountered or until a read + * error occurs. + * + ****************************************************************************/ + +static void consume_eol(int fd, bool consume) +{ + if (consume) + { + int ch; + + do + { + ch = dgetc(fd); + } + while (ch != EOF && ch != '\n'); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_dgets + * + * Description: + * lib_dgets() implements the core logic for both gets() and gets_s(). + * lib_dgets() reads in at most one less than 'buflen' characters from + * fd 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_dgets() will continue reading + * bytes and discarding them until an EOF or a newline encountered or + * until a read error occurs. Otherwise, lib_dgets() returns with the + * remaining of the incoming stream buffer. + * + ****************************************************************************/ + +FAR char *lib_dgets(FAR char *buf, size_t buflen, int fd, + bool keepnl, bool consume) +{ + size_t nch = 0; + + /* Sanity checks */ + + if (!buf || fd < 0) + { + return NULL; + } + + /* Make sure that we have a buffer and space for at least one character */ + + if (buflen < 1) + { + consume_eol(fd, consume); + return NULL; + } + + /* Make sure that we have space for something in addition to the NUL + * terminator. + */ + + if (buflen < 2) + { + *buf = '\0'; + consume_eol(fd, 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 = dgetc(fd); + + /* 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 (ch == '\n') + { + /* Convert \r\n to \n */ + + if (nch > 0 && buf[nch - 1] == '\r') + { + --nch; + } + + 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 == 0) + { + /* Yes.. return NULL as the end of file mark */ + + return NULL; + } + else + { + /* No, terminate the accumulated string */ + + buf[nch] = '\0'; + return buf; + } + } + + /* Otherwise, put the character in the line buffer */ + + else + { + 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(fd, consume); + return buf; + } + } + } +}